VB-Tips September 1, 1992 --------------- Background: This file may be freely shared with others so long as no changes are made to it. Official updates to VB-Tips are available from DL1 of the MSBASIC forum on CompuServe and from the address below. Compiled and Edited by Nelson Ford, 71355,470 Public (software) Library P.O.Box 35705 Houston, TX 77235-5705 800-242-4PsL 713-524-6394 NOTE: The above numbers are NOT bbs's. PsL distributes shareware on disks. New entries in this update are marked with a "|" in the margin in the CONTENTS section. Updated entries are marked with a "}" in the margin. Updates are presently being posted every month or two. Updates are cumulative, so if you get an update, there is no point in keeping the old files. This file would not have been possible without the willingness of others to share their knowledge. Early updates did not include the name of the contributor with each tip, but the following were, and continue to be, among the important contributors: Jonathan Zuck Keith Funk Mark Novisoff Ted Young Dennis Harrington the Microsoft Section Leaders Sysop of the MSBASIC forum on CompuServe is Jerry Fisher, 76711,22. --------------- Using This File: VB-Tips is divided into two files: VB-TIPS.TOC (Table of Contents), and VB-Tips.TXT, the file with the text of the tips. You can view this file with any file viewing utility such as List; however, there is a program named VBTVWR on the MSBASIC forum that can make it easier to work with this file. See VB-Tips Viewer, below. If you do not use the Viewer program, you may want to tack the .TOC file on to the beginning of the .TXT file so that you can access everything at once. If you are using a file viewer like List, first look up information about a topic that you see in the CONTENTS section, you can just search for it. The Category headings (eg: "General") in the CONTENTS section are NOT repeated in the body of the file. You must search on the actual Tip titles. The CONTENTS section is divided into major categories, indicated by an asterisk at the start of the category name, then subcategories, indicated by a colon at the end of the subcategory name, then the tip names, indented under the subcategory name. IMPORTANT: If you cannot find information you need in the CONTENTS, be sure to browse through the text of the "VB Downloadable Files" section at the end of this file. Each Tip is separated from the others by a dashed line. --------------- Using VB-Tips Viewer: OLD VIEWER: The VB-Tips Viewer program was written for viewing this file more easily. See the comments in the Global file of the Viewer code for more information. To save download time, only the source code for VB-Tips was uploaded. You can easily compile it yourself. Feel free to customize the source for your own use, such as hard-coding the location of the VB-TIPS.TXT file on your system. WARNING: If you make changes to the CONTENTS section or any of the Tips captions, the Viewer program may not be able to access this file. Keep a backup. To use the Viewer, select a Category and its topics will be displayed in the adjoining window. Select a Topic (Tip) and it will be displayed in the Tips Window. If you press the Clipboard button, whichever Tip the cursor is in will be copied to the Windows Clipboard. This is the easiest way to copy a routine to your own program. NEW VIEWER: A new, faster Viewing program has been written and is under the name VBTVW2 on the LIB's. For more information, download that file and read the .DOC file. --------------- Disclaimers: While I have done my best to test everything that appears in this file, a couple of typos and other glitches have appeared in previous versions of this file. Anything that is not marked as being new (with a vertical bar in the left margin) should be pretty stable. If you have trouble with any of the new stuff or if you find any errors in this file or if you would like to add info to this file, please send a message on the MSBASIC forum (sec. 5) to 71355,470. The contents of this file are not warranted in any way. I do not assume any responsibility for the use of this materials. I am not affiliated in any way with Microsoft Corporation. --------------- CompuServe Acronyms: Newcomers on the MSBASIC forum and on CompuServe may be puzzled by the acronyms seen in some of the messages. Here are a few of the more popular ones, to help you feel more at home: BTW = By the way FWIW = For what it's worth = "Grin" = Grinning, ducking and running (usually appended to a teasing remark) IMO = In my opinion IMHO = In my *humble* opinion PITA = Pain in the a(natomy) PMFBI = Pardon me for butting in PMJI = Pardon my jumping in ROFL = Rolling on the floor, laughing ;-) = a smiley face winking (tilt your head to the left and look at it) There are many more. You can usually figure them out - or just ask. --------------- Changing Elements of a Control Array: Q: Is there a way to simultaneously change a property in a control array, affecting all the elements simultaneously? For example, if I had an array that had several text labels and I wanted to change all their forecolors to blue. A: No. You have to use a For-Next loop. --------------- Control Array Bug: If you create a control and add code to one of its events and then decide to make it into a control array, VB does not add the "Index As Integer" argument to the control's argument list. When you attempt to run the program, VB gives the error, "Incorrect number of event procedure arguments". (Keith Funk) --------------- Dynamic Arrays: In order to use a dynamic array, you must REDIM it in a module or form: Global: Type SomeType X As Integer etc End Type Global ArrayName() As SomeType Module or Form: Redim ArrayName(x) As SomeType ' x can be a number or a variable --------------- Huge Array Info: Most VB controls (eg: List & Text boxes) and arrays limit you to 64k of data. Microsoft gave beta testers a Huge Array DLL, but did not include it in the finished VB package. The DLL seems to work all right most of the time (see below), but it is difficult to access and slow to load. See HUGEGR.EXE and RANDGR.EXE comparing the use of a Huge Array to a Random Access data file. Performance is about the same, but using a Random Access file means the program starts faster and data is more secure. --------------- Huge Array Problem: If you DIM a Huge Array (a public domain DLL) to a small number of elements and later REDIM it to a large number, you will get an Out Of Memory error from the DLL, even when there is sufficient memory. You can REDIM to a smaller number of elements with no error. Using a 16-byte TYPE variable, I DIMmed the array to 10000 elements and then ReDIMmed it to 23864 elements. This worked fine. However, I could not ReDIM this same array to more than 23864 elements without getting a (-1) Out of Memory error from HUGEARR.DLL, even though if I initially DIM the array to 30000 elements it works fine. - Randy Berry --------------- Resources - Arrays vs Separate Controls: If it's a choice between a sparsely populated control array (5 or less) and the equivalent number of individual controls, then using the individual controls is better. VB's resources and performance are less hit by the individual controls. (Robert Eineigl) --------------- Type Variables: Unlike QuickBASIC, you cannot use arrays within a TYPE...END TYPE declaration, such as: Type SomeType x As Double y(100) As SomeOtherType End Type You can work around it by putting a variable length STRING in your TYPE and then storing the information in the string. Not the easiest way to do things, but in some ways it's more flexible since you don't have to know before hand how large it should be. --------------- Type Variables, Clearing Of: To clear an entire Type in one shot you can dimension a second Type variable of the same kind, and use LSet to assign it: Dim Inv As Invoice, Dummy As Invoice LSet Inv = Dummy (Ethan Winer) --------------- Type Variable Number Placement: Be sure when using arrays with Type variables to put the variable number AFTER the type name and BEFORE the element name. Example: TypeName(i).string1 = "xyz" NOT TypeName.string1(i) = "xyz" --------------- Calling Other Form's Controls: You can invoke a control from another form via the Value property. For example, in Form2, you can say "Form1.Command1.Value = -1" and this will invoke Command1_Click on Form1. Normally, if you have a "(general)" routine (ie: not attached to a control) that you want to access from more than one Form, you should put the code in a Module (see the VB manuals). However, it *is* possible to put the code in one Form and call it from others by the method in the previous paragraph. Instead of putting the routine in "(general)", assign it to a button and set the button's Visible property to False. Now you can call it as above. --------------- Calling Other Form's Subs/Functions: You cannot directly call a sub or function in another form. Form routines are "local" to that routine. You have to move your routines into a module, which makes them "global". One method I use to get around this is to create an invisible textbox or caption on a form, and then in the textbox_chaged sub for that control, call the sub. Its a little obtuse, but it works. Simply address the control: sub text1_change() Call some_neat_sub end sub then inside another form, I use: form1.text1.text="1" REM or whatever the arguments are" This fires the form1.textbox_changed routine, which in turn calls my sub. However, there are lots of good reasons to move routines in a module of thier own (memory, speed etc..) (hank marquis) --------------- Passing Control Arrays: You can send an a single element of a control array to a Sub, but not a whole control array. (Ted Young) Example: For i = 0 to 9 Call SomeSub(SomeControl(i)) Next (Jeff Simms) --------------- Passing Variable Arrays: To pass an array to a subroutines, use enpty parentheses: Call ProcName(Array(), otionalparam1, param2, etc) Inside the called routine you can use LBound and UBound to know how big the array is. (Ethan Winer) --------------- Passing Form & Controls in CALLs: Often overlooked in the manual is how to pass a control in a CALL. The syntax is SUB SomeSub (Lst as Control) Lst.AddItem "abc" In the calling routine, you would say Call SomeSub(List1), for example. You cannot call a Sub that is in another Form. It must be in a module, if not in the calling Form. There is a kludge for getting around this (it involves the principle described in Calling Other Form's Controls, above), but why bother kludging when you can just move the routine into a Module? To pass text from a control, you must use ByVal. Example: Sub SomeSub (s as string) Call SomeSub(Text1.Text) --------------- Passing Properties: If you have a Sub defined like "Sub Something(a%)", you can pass a control's property to it by putting it inside parentheses (in addition to any required parentheses): X = Something((CtlName.ForeColor)) If the Sub is defined using "ByVal", such as "Sub Something (ByVal a%)", then the extra parentheses are not required: X = Something(CtlName.ForeColor) If you need to change the value of a Property in a Sub, you must use the following syntax: Sub ToggleBoolean (Check As Control) Check.Enabled = Not Check.Enabled End Sub Sub Command1_Click () ToggleBoolean Check1 End Sub --------------- Detecting a Directory: Dir$("c:\windows") will be null even if c:\windows directory exists. To get around this, you must append "\." to the directory name: If Dir$("c:\windows\.") Then Print "It's there all right!" (Nelson Ford) --------------- File Copying: 1. Open "FileIn" for Binary as #1 whole = Lof(1) \ 32000 'numer of whole 32768 byte chunks part = Lof(1) MOD 32000 'remaining bytes at end of file buffer$ = string$(32000,0) start& = 1 Open "FileOut" for Binary as #2 for x=1 to whole 'this for-next loop will copy 32,000 get #1, start&, buffer$ 'byte chunks at a time. If there is Put #2, start&, buffer$ 'less than 32,000 bytes in the file, start&= start& + 32000 'whole = 0 and the loop is bypassed. next x buffer$ = string$(part, 0) 'this part of the routine will copy get #1, start&, buffer$ 'the remaining bytes at the end of the put #2, start&, buffer$ 'file. close 2. Here's a routine for file copying using global memory and the new 3.1 functions hread and hwrite which will read/write any size file in one shot as long as sufficeint global memory is available. You'll want to put error handling in as needed. I've included the hread & hwrite declarations. You can get the other function declares and the constants from VBAPI.TXT. Declare Function hread Lib "kernel" Alias "_hread" (ByVal hFile As Integer, lpBuffer As Any, ByVal dwBytes As Long) As Long Declare Function hwrite Lib "kernel" Alias "_hwrite" (ByVal hFile As Integer, lpBuffer As Any, ByVal dwBytes As Long) As Long inHndl% = lopen(InFile$, OF_READ) 'open source file for reading size& = llseek(inHndl%, 0, 2) 'find length of file msg& = llseek(inHndl%, 0, 0) 'reset file pointer to beginning memHndl% = GlobalAlloc(GHND, size&) 'allocate needed global memory memAddr& = GlobalLock(memHndl%) 'get global memory address inBytes& = hread(inHndl%, ByVal memAddr&, size&) 'read in source file outHndl% = lOpen(outFile$, OF_CREATE Or OF_WRITE) 'open target file outBytes& = hwrite(OutHndl%, ByVal memAddr&, size&) 'write out target ok% = lclose(inHndl%) 'close source file ok% = lclose(OutHndl%) 'close target file ok% = GlobalUnlock(memHndl%) 'unlock global memory ok% = GlobalFree(memHndl%) 'free global memory (Dennis Harrington) --------------- Getting File Date & Time: Dennis Harrington, with Costas Kitsos help, came up with the following routine for getting the date and time of an existing file. Global: DefInt A-Z Type OFSTRUCT cBytes As String * 1 fFixedDisk As String * 1 nErrCode As Integer r1 As Integer r2 As Integer szPathName As String * 128 End Type Declare Function OpenFile Lib "Kernel" (ByVal lpFileName as String, lpReOpenBuff As OFSTRUCT, ByVal wStyle) Form1 General: DefInt A-Z Dim FileMonth, FileDay, FileYear Dim FileHour, FileMinute, FileSecond Sub TestMe(Fi$) Dim a As OFSTRUCT x = OpenFile(Fi$, a, &H4000) FileMonth = ((a.r1 And &H7FFF) \ 32) And &HF FileDay = a.r1 And &H1F FileYear = (a.r1 And &H7FFF) \ 512 + 80 FileHour = (a.r2 And &H7FFF) \ 2048 If a.r2 < 0 Then FileHour% = (FileHour% + 16) - 12 'for PM times FileMinute = ((a.r2 And &H7FFF) \ 32) And &H3F FileSecond = (a.r2 And &H1F) * 2 End Sub Sub Form_Click: Call TestMe("c:\config.sys") Print FileMonth "/" FileDay "/" FileYear Print FileHour ":" FileMinute ":" FileSecond End Sub --------------- Getting File Info: VB does not let you get detail about files, such as file date, size, attribute. (Except through API calls. See above.) Look for VBDOS on DL1 or DL6. Third-party libraries offer these functions too. Look for demos of QuickPak for Windows and VB-Muscle. --------------- Searching PATH$: For an example of how to search the directories in the DOS Path for a file, see the ICONWRKS.BAS module's "Help_File_In_Path". (ICONWRKS is one of the sample files that comes with VB.) --------------- Btrieve: There are the DLL-Declarations for Btrieve. The requestor must be loaded before Windows: Declare Function wbtrvinit Lib "wbtrcall.dll" (ByVal init$) As Integer Declare Sub wbtrvstop Lib "wbtrcall.dll" () Declare Function btrcall Lib "wbtrcall.dll" (ByVal a%, ByVal b$, ByVal c$, d%, ByVal e$, ByVal f%, ByVal g%) As Integer FIELD and VARPTR are definitely not necessary. You'll need to set up a user-defined type to work with Btrieve (instead of FIELD). The call syntax for Win Btrieve takes care of pointing to the record (instead of VARPTR). Just be sure to pass any variable-length strings (i.e, A$) BYVAL. Owners of versions of Btrieve no longer supported, including the old single-user DOS version, can upgrade to Btrieve for Windows for $125. If you have a still-supported version, such as Btrieve/N, you cannot upgrade. To upgrade, call 800-RED-WORD. If you are using Btrv-Win with a network, the Btrv requestor must be loaded before Windows, but for single-user set-ups, you do not need anything but the Btrieve DLL. --------------- Deleting Data in Sequential File: Here's an idea for deleting text in the middle of a sequential file that might work. Haven't tried it, but it seems like it would work and wouldn't be difficult to implement. First, you'll have to save file pointers to the beginning of each message (e.g., in a Long Int array during message load using the SEEK function) in MsgPtr&(). Now, if you want to delete message 50 and there are a total of 200, you'd do the following: MsgToDelete = 50 TotMsgs = 200 BlockSize& = 32768&'-- make this larger or smaller to find optimum size Temp$ = Space$(BlockSize&) Open "MESSAGE.MSG" For Binary As #1 SizeOfFile& = LOF(1) SizeToMove& = SizeOfFile& - MsgPtr&(MsgToDelete + 1) NumBlocks = SizeToMove \ BlockSize& LeftOver = SizeToMove& - Num32KBlocks * BlockSize& FromLoc& = MsgPtr&(MsgToDelete + 1) ToLoc& = MsgPtr&(MsgToDelete) For i = 1 To NumBlocks Seek #1, FromLoc& + BlockSize& * (i - 1) Get #1, Temp$ Seek #1, ToLoc& + BlockSize& * (i - 1) Put #1, Temp$ Next Temp$ = Space$(LeftOver) Seek #1, FromLoc& + BlockSize& * NumBlocks Get #1, Temp$ Seek #1, ToLoc& + BlockSize& * NumBlocks Put #1, Temp$ '-- Now adjust the MsgPtr& array TotMsgs = TotMsgs - 1 Adjust = MsgPtr&(MsgToDelete + 1) - MsgPtr&(MsgToDelete) For i = MsgToDelete To TotMsgs MsgPtr&(i) = MsgPtr&(i + 1) - Adjust Next --------------- MKI$ & CVI in VB: function MKI$ (Work%) MKI$ = Chr$ (Work% MOD 256) + Chr$ (Work% \ 256) end function function CVI% (Work$) CVI% = (ASC (Left$ (Work$, 1)) * 256) + ASC (Right$ (Work$, 1)) end function --------------- Objectrieve: ObjectTrieve/VB seems to be the first data manager available to offer Binary Large Objects (BLOBS). ObjectTrieve is an ISAM file manager based on the X/OPEN standard. You may want to consider calling Coramandel at 1 800 535-3267 or 718-793-7963. Ask for Narayan Laksham --------------- Control Must Be Visible: If you get an illegal function call when trying to SetFocus to a control, the reason is probably that the control/form is not visible. This usually happens in Form_Load. Just add the line: "Form1.Show". (Mark Novisoff) --------------- GotFocus Lost Due to MsgBox: Putting a MsgBox in a Control's LostFocus keeps the GotFocus in the next Control from being executed: Sub Text1_LostFocus () MsgBox = "Losing Focus" Sub Text2_GotFocus () Text2.Text = "Got Focus" This appears simply to be a bug in VB. --------------- LostFocus Bug: If you have specified a lost focus procedure it is possible that when the focus is lost, your form will re-load itself if the string being examined is a null string. It took me awhile to find the problem, but if I commented out my code, sure enough everything worked fine. I got around it by first specifying in the lost focus routine that the string I was checking must not be a null string (if so, exit sub). (Jim Poland) --------------- LostFocus Timing: If one control has focus and you execute another control by clicking on it, control-1's LostFocus gets executed before control-2's Click event, but if you use an access key to execute the second control or if you use SetFocus to change focus, then control-1's LostFocus gets executed AFTER control-2's Click event. --------------- 64k Limit: Global.Bas is limited to 64k. If you run into that limit, move some of the code into modules. --------------- DEFINT Is NOT Global: DEFINT A-Z in your Global file does NOT have a GLOBAL effect. You must do a DEFINT A-Z in the General_Declarations of each form. --------------- Global Const CRLF = chr$(13) + chr$(10) Not Allowed: Several people have complained about the above command not being allowed. (You cannot use CHR$ in a Global Const.) This is not a bug, but a limitation of the language. Use the following instead: GLOBAL.BAS Global CRLF$ Sub Form_Load() ' Or Sub Main, if you prefer CRLF$ = CHR$(13) + CHR$(10) End Sub --------------- Global.bas Renaming: VB will not let you load a file and rename it to Global.bas, so you can cut-and-paste code, but it's easier to use the Save Text to save the desired Global.bas code and then use Load Text (in the Code menu) to Replace or Merge it into the new Global.bas. (Jon Wumkes) --------------- WINAPI - "Out of Memory": WINAPI contains all the Declares for the API for VB. However, you can NOT simply load this file into Global.Bas or you will get an out of memory error. (It is too large, for one thing.) Instead, load it into Write or some other editor or word processor with sufficient capacity and cut-paste just the Declares that you need into Global. A cross-reference file for WINAPI is now available: APIXREF.HLP. To access this file, while in VB press F1 for Help, then File-Open from the menu to load the file. --------------- Alt-F4-Exit Trapping: To stop the user from quitting your app by pressing Alt-F4 (the shortcut key for the System box Close), you can put Cancel=-1 in Form_Unload, but that also keeps you from unloading the form when you really want to. You can get around this by setting a global flag/variable before unloading the form and checking the variable in the Unload sub. Another alternative is to remove Close from the Control box. See "Control Box Menu, Removing Items" under "Programming - System". --------------- Calling the Same Routines: The easy way to get both the Mouse Click and the Enter key to do the same thing is to write a third sub that you call from the other two: Sub CommonHandler ' Do your thing here End Sub Sub Ctlname_Click() Commonhandler End Sub Sub Text1_KeyPress(....) If KeyAscii = 13 Then KeyAscii = 0 CommonHandler End If End Sub --------------- Ctrl-Alt-Del Can't be Trapped: Ctrl-Alt-Del cannot be trapped in VB. --------------- Cursor Positioning: It's often desirable to be able to put the user's mouse cursor on the button or other location where he needs to click next. The following code lets you do that: Global.Bas: DefInt A-Z Type RECT left as Integer top as Integer right as Integer bottom as Integer End Type Declare Sub GetWindowsRect Lib "User" (ByVal hWnd, lpRect As Rect) Declare Sub SetCursorPos Lib "User" (ByVal x, ByVal y) Declare Function GetFocus Lib "User" () Form1.General_Declarations: DefInt A-Z Dim cRect As RECT PositionCursor Command1 Sub PositionCursor(C as Control) C.SetFocus 'get control's coords in screen pixels: GetWindowRect GetFocus(), btn_Rect 'calculate coords for the center of the control: x = cRect.Left + (cRect.Right - cRect.Left) / 2 y = cRect.Top + (cRect.Bottom - cRect.Top) / 2 SetCursorPos x, y (Nelson Ford) --------------- Cursor Screen Coordinates: You can get the cursor screen coordinates and use them to determine if you are over a particular control, etc.: Type POINTAPI x As Integer y As Integer End Type Declare Sub GetCursorPos Lib "User" (lpPoint As POINTAPI) Global lpPoint As POINTAPI You'd have to use Screen.ActiveControl to decide which control you are over but this reports screen coordinates regardless of which control you are over. (Robert Eineigl) --------------- Double-Click before Click, Trapping: If you have code assigned to both the Click event AND the Double-Click, when you double-click, VB will execute the Click code on the first click of the double and the D-C code on the second click. Here is a routine for letting you have both (Nelson Ford): General_Declarations: Dim DblClk, Reject Sub Form_Load() For i = 1 to 20: List1.Additem Str$(i): Next End Sub Sub List1_Click() If Reject Then Exit Sub Else Reject = True x! = Timer: Do: y = DoEvents(): Loop While Abs(Timer - x!) < .3 'The line above pauses to allow some time for the 2nd click, if any. 'You can play with this number to get best results. The larger the 'number, the longer the delay when doing a single click. The smaller 'the number, the more likely you'll generate an unwanted single click 'execution when trying to do a double click. If DblClk Then DblClk = 0: Exit Sub Print "Single Click" Reject = 0 End Sub Sub List1_DblClick() Print "Double Click" DblClk = - 1 'it does help to actually *set* this variable Reject = False End Sub --------------- INKEY$ Loop Emulation: In other Basics, you can say: WHILE INKEY$ = "" [do stuff] WEND VB does not support INKEY$, so here's how to emulate the concept of looping until a key is pressed: GLOBAL.BAS: DefInt A-Z Declare Function PeekMessage Lib "User" (ByVal Msg$, ByVal hWnd, ByVal Min, ByVal Max, ByVal Flag) Const WM_KEYFIRST = &H100 Const WM_KEYLAST = &H108 Const Msg$ = "XXXXXXXXXXXXXXXX" Function NoKeys () Result = PeekMessage(Msg$, 0, WM_KEYFIRST, WM_KEYLAST, 1) NoKeys = Result -1 End Function SomeSub (): While NoKeys () [do stuff] Wend End Sub This function does *NOT* yield to other Win apps like DoEvents does, if you want the same functionality then the last parameter passed to PeekMessage should be zero (0). -=- Jonathan Zuck --------------- Joystick & VB: You cannot read the joystick port with native VB commands. See MHELP in the program listing at the end of this file for a DLL that lets you emulate INP and OUT statements that should do the job. --------------- Keyboard/Mouse Shortcuts: Fm: Chris Sacksteder 1. To show a control's first event procedure, double-click on the control. 2. Use tab to cycle through controls. 3. Ctrl+up and Ctrl+down can be used to quickly cycle through procedures. 4. Ctrl+right and Ctrl+left move the cursor by word. 5. Ctrl+Y cuts the current line to the clipboard. 6. Ctrl+N opens a blank line before the current line. 7. double-click on a word to mark the whole word. 8. select a procedure name (double click on it) and press Shift+F2 to jump to that procedure. 9. select several lines and press Tab to indent all of them; Shift+Tab will undent them 10. Click-drag down the top of a code box to open a second code box. 11. use the keyboard to jump to the first letter of any list (controls, procedures, properties) 12. Get help on any keyword by placing the carret anywhere in the word and pressing F1 (in the Immediate window you have to select the entire keyword before pressing F1). 13. If you get an error during runtime, press F1 to get more information. --------------- Mouse Move Event: The _MouseMove event is only triggered when the mouse changes position. However, there's a bug in VB which causes continuous MouseMove events when you're using debug.print within the MouseMove event. (Ted Young) --------------- Mouse Pointer Style Setting: Each form has it's own mouse pointer. If you want to keep the pointer as an hourglass, for example, while different forms are loaded, you need to place a "_.MousePointer = 11" statement in each Form_Load. Alternatively, you can use the Screen.MousePointer object/property to set the hour glass for all forms in the Project. (It does not affect the MousePointer for other Windows applications that you may be running at the same time.) --------------- Right Click - Can't Detect: VB provides no direct means of detecting a double-click of a right mouse button. --------------- Uppercase Input Forced: You may use either the KeyPress event to convert the keys to uppercase, or you can do it through the Window's API. The API is a little more complicated, but I prefer it. You need the following: Global Const GWL_STYLE = (-16) Global Const ES_UPPERCASE = &H8& Declare Function GetWindowLong& Lib "User" (ByVal hWnd As Integer, ByVal nIndex As Integer) Declare Function SetWindowLong& Lib "User" (ByVal hWnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Long) Declare Function ControlhWnd% Lib "Ctlhwnd.dll" (Ctl As Control) ' The Ctlhwnd.dll file is available on CIS or from PsL ' or search this file for GetFocus and use it instead. Sub TextUpper (Ctl As Control) Dim StyleFlags As Long Dim Handle As Integer Handle = ControlhWnd(Ctl) ' get window handle StyleFlags = GetWindowLong(Handle, GWL_STYLE) StyleFlags = StyleFlags Or ES_UPPERCASE ' add uppercase style StyleFlags = SetWindowLong(Handle, GWL_STYLE, StyleFlags) End Sub (Roger Jensen) --------------- Using "Enter" in Place of "Tab": Sub Text1_KeyPress(KeyAscii As Integer) If KeyAscii = 13 Then KeyAscii = 0 ' Avoid the beep SendKeys "{Tab}" ' Simulate pressing the Tab key End If End Sub --------------- Aligning Text of Different Sizes: By default, windows aligns text characters based on the top of each character's cell. If you use several fonts, this can result in some funny looking typography because their base lines don't lineup, even if they're the same point size. By using the API call SetTextAlign to reset the text alignment to TA_BASELINE which aligns characters on their typographical baselines, you can mix different fonts and different sizes and they line up correctly. If you do this, remember that with base aligned text, CurrentY is the text baseline and not the top of the character cell. Printing the first line with CurrentY = 0 will only print the descenders of text characters on that line. (Dennis L. Harrington) --------------- Calculating Page Length: If you use GetDeviceCaps to determine the printable page size, you know the actual printable page length. It's then easy to adjust your printing to any length paper. Compute the number of verticle twips on the page and subtract whatever you want for top and bottom margins and 1 line of text. Then just print from some form of loop and increment the CurrentY yourself by the number of verticle twips required for each line. Each time you increment the CurrentY, test the new value against the maximum computed above. If the new CurrentY is greater than the maximum, you've filled the page and it's time to start a new page. This is especially handy with laser printers may be use either letter or legal size paper. (Dennis L. Harrington) --------------- Changing Paper Bins: The following is a routine to change paper bins between pages on a laser printer (Dennis L. Harrington): In Global Module: DefInt A-Z Type BinInfo BinNumber as Integer nBins as Integer res as string*8 End Type Declare Function Escape Lib "GDI" (ByVal hDc%, ByVal nEscape%, ByVal nCount%, Indata as Any, OutData as Any) as Integer Global Const GETSETPAPERBINS = 29 Code for changing the paper bin: Dim BinIn as BinInfo BinIn.BinNumber = {desired bin number} or &H8000 'NOTE: You must enter the appropriate bin number for the printer in use. 'On a NEC LC890 for example, the top bin is 1 and the bottom bin is 3. 'Using "or &H8000" makes the change immediate for the next page. 'Omit "or &H8000" and the change does not occur till the next print job. e% = Escape(printer.hDC, GETSETPAPERBINS, 12, BinIn, 0&) Code for reading current paper bin status Dim BinOut as BinInfo e% = Escape(Printer.hDc, GETSETPAPERBINS, 12, 0&, BinOut) Current_Bin_Num% = BinOut.BinNumber Num_Available_Bins% = BinOut.nBins Note: The value for BinNumber is a printer-dependent value. For example, on an NEC LC890 in postscript mode, BinNumber = 1 uses the upper bin and BinNumber = 3 LaserJet IID, BinNumber = 0 uses the upper bin and BinNumber = 1 uses the lower bin. Thus, you need to figure out which BinNumber relates to which tray for the printer and use those numbers. --------------- Color Printing: VB does not directly support printing in colors on color printers. The following API call may be used: Declare Function SetTextColor Lib "GDI" (ByVal hDC As Integer, ByVal crColor As Long) As Long r&=SetTextColor(Printer.hDC,QBColor(4)) Printer.Print "Hello" (Robert Eineigl, Microsoft Product Support) --------------- Default Printer Changing: Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Any) As Integer NewPrinter$ = "HP LaserJet III,hppcl5a,FILE:" OldPrinter$ = GetProfileString("windows","device","") Result = WriteProfileString("windows","device",NewPrinter$) Const wm_WinINIChange = &H1A x& = SendMessage(&HFFFF,wm_WinINIChange,0,ByVal "windows") Printer.Print etc, etc, Result = WriteProfileString("windows","device",OldPrinter$) x& = SendMessage(&HFFFF,wn_WinINIChange,0,ByVal "windows") --------------- EndDoc Makes Temp Files: When you do EndDoc, VB makes a NEW temporary file for the new page. So if you use NewPage to do page ejects, say for printing 1500 labels, then you get 1500 temporary print files! --------------- EndDoc vs NewPage: Use NewPage between pages (if required) and EndDoc at the end of the print. This will keep another app from printing in the middle of a long printout. There is NO need to put NewPage just before the EndDoc. Put DoEvents in your print routine. This will let other apps print, but they still won't break up your document. The Windows print buffer and print manager are smart enough to know to keep documents together. (Jack Presley) --------------- Font Not Available on Printer: If your VB app uses a font that is not available on a user's printer, the next closest font (based on size) will be used instead. (Robert Eineigl, MS) --------------- Formfeed Problems: I am writing a label printing routine in VB, and in order to align the labels, I want to be able to print only one test label. The problem I am seeing is that the label won't print until I either close the program completely, or send a Printer.NewPage or Printer.EndDoc. However, both of these force a page feed (11 inch type) which wastes the next 10 labels. How can I close the Print Manager file to force a printout of only 3 lines? --------------- Getting Available Font Sizes: If using PostScript, TrueType, ATM or other scalable fonts, then any size is available. For raster (fixed-size) fonts, you can use the following: For i% = 1 To 30 FontSize = i% If FontSize <> LastFontSize Then Print FontSize LastFontSize = FontSize End If Next This will show you the font sizes that windows will *produce* for you. Note: o this is NOT the list of exact fonts available o you *could* miss some sizes here, so if you need more precision then loop with something like For i! = .5 To 30 Step .5 --------------- Getting List of Installed Printers: buf$ = String$(2048, 0) BufSize% = len(buf$) y% = GetProfileString("devices", ByVal 0&, "Error", buf$, BufSize%) You'll need to parse through buf$ to find all of the printers. Chr$(0) will delimit each printer entry. --------------- Getting Printer Settings: DefInt A-Z Type DEVMODE DeviceName As String * 32 SpecVersion As Integer DriverVersion As Integer Size As Integer DriverExtra As Integer members As Long Orientation As Integer PaperSize As Integer PaperLength As Integer PaperWidth As Integer Scale As Integer Copies As Integer DefaultSource As Integer PrintQuality As Integer Color As Integer Duplex As Integer For Win 3.1: '''''''''''' Declare Function ResetDC Lib "GDI" (ByVal HDC, lpDevMode As DEVMODE) Sub Command1_Click () Dim OrientBuff As DEVMODE OrientBuff.Orientation = 2 OrientBuff.members = 1 OrientBuff.SpecVersion = &H300 OrientBuff.DeviceName = "HP LaserJet Series II" OrientBuff.Size = Len(OrientBuff) ApiErr = ResetDC(Printer.hdc, OrientBuff) Debug.Print ApiErr, Printer.hdc For ncount = 1 To 5 printer.Print String$(30, Chr$(64 + ncount)) Next Printer.EndDoc End Sub For Win 3.0: '''''''''''' Declare Function escape Lib "gdi" (ByVal HDC As Integer, ByVal nEscape As Integer, ByVal ncount As Integer, lpInData As Any, lpoutdata As Any) As Integer Sub Form_Click () Dim RtnCode As Integer Dim ncount As Integer Static Orient(0 To 4) As Long Orient(0) = 2 RtnCode = escape(printer.HDC, 30, 10, Orient(0), 0&) For ncount = 1 To 5 printer.Print String$(30, Chr$(64 + ncount)) Next printer.EndDoc End Sub (Tony G. Strain) --------------- Landscape Setting on HPLJ: The following code lets you change a LaserJet to landscape mode. (From Dennis L. Harrington) GLOBAL: Type ORIENT Orientation as Long Pad as string * 16 End Type Declare Function PrtOrient% Lib "GDI" Alias "Escape" (ByVal hDc%, ByVal nEsc%, ByVal nLen%, lpData as ORIENT, lpOut as Any) Global prtO as Orient Sub Form_Click prtO.Orientation = n 'n=1 for portrait or 2 for landscape x% = PrtOrient%(Printer.hDC, 30, len(prtO), prtO, 0&) Printer.EndDoc '- required to make the change take effect immediately. 'EndDoc usually generates a FormFeed, which would be unwanted here, but in 'this case, coming right after the PrtOrinet line, it does not. Thanks to 'John Kesler for pointing this out. Printer.Print "whatever text you want to print goes here" Printer.EndDoc 'This EndDoc does do a FF. End Sub --------------- Laser Printer Margins: The amount of margin (unprintable area) varies with different laser printers and cartridges. One solution is to assume a larger than .25" margin. (George Campbell) Another solution is to use the Printer.ScaleWidth and Printer.ScaleHeight properties instead of Printer.Width & Printer.Height for calculating printable area. (Bryon Scott) But be careful with these two properties, especially if you're setting Printer.CurrentX and CurrentY. If you change these figures, you revert to the User setting for ScaleMode, and that can throw your printing off. It's OK, however, to read these properties. (George Campbell) Sample code (Don Funk): Dim PageNo As String Header$ = "Yess" Printer.CurrentX = 0 Printer.Print Date$; Printer.CurrentX = (Printer.ScaleWidth - Printer.TextWidth(Header$)) Printer.CurrentX = Printer.CurrentX / 2 Printer.Print Header$; PageNo = "Page: " + Format$(12, "###") Printer.CurrentX = Printer.ScaleWidth - Printer.TextWidth(PageNo) Printer.Print PageNo Printer.Print " " Printer.EndDoc --------------- LaserJet Driver Update: The latest version of the LJ III printer driver is 3.86. There were a bunch of bugs fixed in this version. It is available over in the HPPER forum LIBS on CompuServe. --------------- LaserJet Lost Line Fixed: Using the File menu Print command to print source code will truncate one line of code per page of output when printing to a Hewlett-Packard (HP) LaserJet series III printer using the HPPCL5A.DRV printer driver. This is a problem with the Hewlett-Packard LaserJet series III printer driver version 3.42 for Windows. Microsoft has confirmed this to be a problem with the HPPCL5A printer driver version 3.42. This problem was corrected by the HP III driver version 30.3.85 included with Microsoft Word for Windows version 2.0. --------------- LaserJet Offset: VB uses the whole page for figuring offsets, but the HP LaserJet driver, or the printer itself, uses the printable area, which is about .25 less on each side of the paper. You must allow for this when trying to place printing precisely on the page with a LaserJet printer. --------------- Matching Printer & Screen Fonts: Use an On Error Resume Next line before sending the printer font name stuff to the screen. If you try to display a printer font for which there's no screen equivalent, you'll get an Invalid Property Value error. If, however, you use the On Error line to ignore the error, the Windows GDI will attempt it's very best to display your font. It actually does a pretty good job, although it won't be an accurate representation. Same thing applies when setting FontSize, etc. for the printer. If you bypass the setting with the On Error line, the printer will go ahead and print, but in the font's default size. It's sloppy programming, but it works. Just be sure to reset your error handling after doing this, or your program will crash without a doubt, and you won't be able to spot your other errors. With ATM, if a font family doesn't have a separate BOLD, ITALIC, or BOLD ITALIC member, you can still print bold, italic and bold italic characters. I believe it's the Windows GDI which takes care of that job, but I'm not certain. The quality won't be as good as if you actually had the outlines available, but it's not bad either. You should see Cooper Black in this fake Boldfacing. (George Campbell) --------------- Not Printing?: If your print routines never make it to the printer, make sure you have done the following: 1. Make sure you send the Printer.EndDoc property at the end of the print job. 2. If you are in a long print job, add "x = DoEvents ()" to allow the Windows Print Manager to get control. 3. If you're using a PostScript printer, the Printer.EndDoc isn't enough. You need two lines: Printer.NewPage Printer.Enddoc Without that, you'll put your PS printer into a lock. Don't put anything between the two commands, or you'll get a blank page. --------------- Number of Copies, Setting: The following routine lets you set the number of copies to be printed: Declare Function Escape% Lib "GDI" (ByVal hDC%,ByVal nEscape%, ByVal nCount%, lplnData As Any, lpOutData As Any) Global Const SETCOPYCOUNT = 17 Sub Command1_Click () Printer.Print "" NumCopies% = 3 'an example to print 3 copies ActualCopies% = 3 X% = Escape%(Printer.hDC, SETCOPYCOUNT, Len(i%), NumCopies%, ActualCopies%) Printer.Print Text1.Text Printer.EndDoc End Sub --------------- Printer Setup: Declare Function LoadLibrary Lib "kernel" (Byval LibName$) As Integer Declare Function FreeLibrary Lib "kernel" (hLib%) As Integer Declare Function DeviceMode Lib "HPPCL.DRV" (hWnd%, hLib%, Dev$, Port$) SUB Command1_Click () hLib% = LoadLib% ("HPPCL.DRV") Result% = DeviceMode (hWnd, hLib%,"HP / PCL LaserJet","LPT1:") Result% = FreeLibrary (hLib%) END SUB Declare Function LoadLibrary Lib "KERNEL" (ByVal lpLibFileName$) As Integer Declare Sub FreeLibrary Lib "KERNEL" (ByVal hLibModule%) Then change PSetupMNU_Click to read: Sub PSetupMNU_Click () RetStr$ = String$(256, 0) RetStrSize% = Len(RetStr$) x% = GetProfileString("windows", "device", "", RetStr$, RetStrSize%) i% = InStr(RetStr$, ",") If i% > 0 Then a$ = Left$(RetStr$, i% - 1) b$ = Right$(RetStr$, Len(RetStr$) - i%) j% = InStr(b$, ",") If j% > 0 Then DriverName$ = Left$(b$, j% - 1) PortName$ = Right$(b$, Len(b$) - j%) End If End If If Len(DriverName$) > 0 And Len(PortName$) > 0 Then LibHandle% = LoadLibrary("PSETUP.DLL") If LibHandle% >= 32 Then r% = DoPrinterSetup(Form1.hwnd, DriverName$, PortName$) FreeLibrary LibHandle% If Not r% Then MsgBox "Can't run Printer Setup", 64, "Printer Setup" End If Else MsgBox "No default printer selected", 64, "Printer Setup" End If End Sub --------------- Printer Control Codes: The normal Print command in VB will not properly send printer control codes through to the printer. Here is how to get around VB: Open "LPT1" For Binary As #1 'note: no colon on LPT1 Put #1,, Close #1 --------------- Printer Wordwrap: The following shows how to print text to a printer/form using the line wrapping built into a Text box. By calling the API calls, you can printout individual lines of text to the form/printer. You only have to add the CurrentX/CurrentY to control the location of the print out. This example will need one Text box and one Command button. Make sure that you set the CurrentX/Y to some location on the form that isn't being covered up by the Text box or Command control. Declare Function GetFocus% Lib "user" () Declare Function SendMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, ByVal lParam As Any) Sub Command1_Click () Dim i As Long CurrentY = 1000 NumberOfLines = GetLineCount(Text1) For i = 0 To NumberOfLines Form1.CurrentX = 1000 Print LineOftext$(Text1, i) Next i End Sub Function LineOftext$ (TextBox As Control, LineNumber As Long) '* This function copies a line of text specified by LineNumber '* from the edit control. The first line starts at zero. Const MAX_CHAR_PER_LINE = 80 Const EM_GETLINE = &H414 TextBox.SetFocus Buffer$ = Space$(MAX_CHAR_PER_LINE) API_Error% = SendMessage(GetFocus(), EM_GETLINE, LineNumber, Buffer$) LineOftext$ = Buffer$ End Function Function GetLineCount& (TextBox As Control) '* This function will return the number of lines in the edit '* control. Const EM_GETLINECOUNT = &H40A TextBox.SetFocus Pos% = SendMessage(GetFocus(), EM_GETLINECOUNT, 0&, 0&) aGetLineCount.Caption = "GetLineCount = " + Str$(Pos%) fGetLineCount = Pos% End Function --------------- Printing Forms: In VB's File menu, there is a Print option for printing forms and code. On an HPLJ, the top of forms will be cut off because VB/Windows tries to print in the top-most part of the page, which is, of course, unusable on an HPLJ. MS acknowledges the problem but has no solution other than printing to disk and then using another program to print the disk file. Also see LaserJet Lost Line Fixed, above. --------------- Printing Problems: Fm: George Campbell 1. The StretchBlt and BitBlt tools don't work on Postscript printers. 2. Also, the same tools fail to work with ICO or WMF files. The images appear on the screen, but will not print. --------------- ScaleLeft Errors: You have to be careful with ScaleLeft and the other similar properties. If you set them at runtime, your ScaleMode reverts to the User setting. What this means is that setting any of those properties destroys your original ScaleMode settings. The documentation on this subject is a disaster. I spent almost an entire day fooling with those properties, only to give up trying to use them for setting margins on the Printer object. Probably the best option is to treat ScaleLeft, etc. as read-only at runtime. (George Campbell) --------------- Sending Raw Data to the Printer: Using the PASSTHROUGH Escape function, one *should* be able to print raw data on a printer. There has been some discussion about some Postscript drivers not supporting the PASSTHROUGH Escape. I don't know. The PASSTHROUGH is kind of tricky because of the info required to pass in the string to print. This should work... 'Declaration section Declare Function Escape Lib "GDI" (ByVal hDC As Integer, ByVal nEscape As_ Integer, ByVal nCount As Integer, lplnData As Any, lpOutData As Any) As_ Integer ' all on one line Const PASSTHROUGH = 19 String2Pass$ = "This is the string to print." InData$ = Chr$(len(String2Pass$) Mod 256) + Chr$(Len(String2Pass$) \ 256) InData$ = InData$ + String2Pass$ Printer.Print " "; ' If you've already printed since last EndDoc, this isn't ' necessary. Result% = Escape(Printer.hDC, PASSTHROUGH, Len(InData$), InData$, 0&) Debug.Print Result% ' length of string actually passed to printer. Printer.EndDoc The calculation to set the first two bytes of InData$ is simpler than it looks. The first byte is the low byte of the string length to send. If it's less than 256 you can just set it to the Chr$(Len(String2Pass$)) + Chr$(0). The high byte would be used if it's longer than 255. (Jack Presley) --------------- "Printer.TextHeight" Does Formfeed: Q: The code "X = Printer.TextHeight("Sample")" causes Print Manager to open, and after exiting the VB program, my printer does a form feed. All I want to do is set a variable to the TextHeight, but it ejects a piece of paper. (Paul Davison) A1: In the file EndPrn (see program listings at the end of this file) is a method of cancelling a print job. Use this routine to cancel the blank page. It takes about half a second. (George Campbell) A2: Global: DefInt A-Z Const ABORTDOC=2, Const NULL = 0& Declare Function Escape Lib "GDI" (ByVal hDC, ByVal nEscape, ByVal nCount, lplnData As Any, lpOutData As Any) Form: DefInt A-Z ... printer.scalewidth = 3000 hite& = printer.TextHeight("I") wit& = printer.TextWidth("I") Print "hite& "; hite& Print "wit& "; wit& x% = Escape(printer.hDC, ABORTDOC, NULL, NULL, NULL) Print "ABORT"; x% The above code worked on PostScript and PCL laser printers. On the PS printer, ending caused a "Printer Error" in VB, and that error plus "Can't Quit At This Time" error (which nonetheless allowed me to quit after hitting OK) when compiled. (Robert Eineigl, MS) --------------- TextWidth Improvement: The following function beats the TextWidth method all to heck. It returns the width to 1/1000th of a point with complete accuracy, is device independent, accepts very long strings, doesn't initialize the Print Manager, and doesn't send a header to the printer. (Walt Wooton) Declare Function GetTextExtent Lib "GDI" (ByVal hDC%, ByVal lpString$, ByVal nCount%) As Long Function WidthOfText (text$) As Single r% = GetDeviceCaps%(Printer.hDC, 52) s% = Printer.FontSize Printer.FontSize = 1000 Maxchars& = Int(65535 / (r% / 72) / 1000) length% = Len(text$) j% = Abs(Int(length% / Maxchars&)) For k% = 0 To j% textpart$ = Mid$(text$, 1 + (Maxchars& * k%), Maxchars&) i& = GetTextExtent(Printer.hDC, textpart$, Len(textpart$)) a& = 65535 And i& b& = b& + a& Next k% Printer.FontSize = s% WidthOfText = CInt(b& * (72 / r%)) * .001 * s% End Function --------------- Background for VB: Many people have complained about VB not having a background and that seeing the Program Mangaer and/or wallpaper in the background is distracting. One work-around would be to compile a simple VB app with a form that fills the screen up to the space at the top where the VB menu bar appears. Have this app shell to VB to get it going. An alternative to having a full-screen form would be to have an app that minimizes the Program Manager and uses the routine in this file to load a blank wallpaper file (a BMP which you can easily create with Paintbrush) and then shell to VB. --------------- Color Const Declarations: The following are VB Global Const definitions for the 48 default colors on the palette. The constant definitions correspond to the color palette when viewing the palette from top-to-bottom and left-to-right. (Steve Schmidt) Global Const Black0 = &HFFFFFF Global Const Black2 = &HC0C0C0 Global Const Black3 = &H808080 Global Const Black1 = &HE0E0E0 Global Const Black4 = &H404040 Global Const Black5 = &H0& Global Const Red0 = &HC0C0FF Global Const Red1 = &H8080FF Global Const Red2 = &HFF& Global Const Orange0 = &HC0E0FF Global Const Orange1 = &H80C0FF Global Const Orange2 = &H80FF& Global Const Yellow0 = &HE0FFFF Global Const Yellow1 = &H80FFFF Global Const Yellow2 = &HFFFF& Global Const Green0 = &HC0FFC0 Global Const Green1 = &H80FF80 Global Const Green2 = &HFF00& Global Const Cyan0 = &HFFFFC0 Global Const Cyan1 = &HFFFF80 Global Const Cyan2 = &HFFFF00 Global Const Blue0 = &HFFC0C0 Global Const Blue1 = &HFF8080 Global Const Blue2 = &HFF0000 Global Const Magenta0 = &HFFC0FF Global Const Magenta1 = &HFF80FF Global Const Magenta2 = &HFF00FF Global Const Red3 = &HC0& Global Const Red4 = &H80& Global Const Red5 = &H40& Global Const Orange3 = &H40C0& Global Const Orange4 = &H4080& Global Const Orange5 = &H404080 Global Const Yellow3 = &HC0C0& Global Const Yellow4 = &H8080& Global Const Yellow5 = &H4040& Global Const Green3 = &HC000& Global Const Green4 = &H8000& Global Const Green5 = &H4000& Global Const Cyan3 = &HC0C000 Global Const Cyan4 = &H808000 Global Const Cyan5 = &H404000 Global Const Blue3 = &HC00000 Global Const Blue4 = &H800000 Global Const Blue5 = &H400000 Global Const Magenta3 = &HC000C0 Global Const Magenta4 = &H800080 Global Const Magenta5 = &H400040 --------------- Copying the Desktop to a Picture Box: This routine is useful for a number of purposes, such as creating a "see-through" box by BitBlt-ing the part of the Desktop underlying a form onto that form to make it look like you can see through it. Global.Bas: DefInt A-Z Declare Function BitBlt Lib "GDI" (ByVal hDestDc, ByVal X, ByVal Y, ByVal nWidth, ByVal nHeight, ByVal hSrcDC, ByVal XSrc, ByVal YSrc, ByVal dwRop As Long) Global Const SRCCOPY& = &HCC0020 Declare Function CreateDC Lib "GDI" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As String) As Integer Form1_Load: ScreenDC = CreateDC("DISPLAY", Chr$(0), Chr$(0), Chr$(0)) Form1.Height = Screen.Height Form1.Width = Screen.Width Form1.Left = 0 Form1.ScaleMode = 3 Form1.autoredraw = -1 x = BitBlt(Form1.hdc, 1, 1, Screen.Width, Screen.Height, ScreenDC, 1, 1, srccopy&) Form1.Left = 1 Form1.top = 1 Form1_Click 'After running this, the user can't tell that he's not still at the 'desktop, so you could make a great joke app by putting some funny 'stuff in the click event. --------------- Displaying "ASCII Graphics": In February 1992's Inside Visual Basic, we printed some code that let's you output text using the OEM character set (aka extended ASCII characters): DefInt A-Z Declare Function GetStockObject Lib "GDI" (ByVal nIndex) Declare Function SelectObject Lib "GDI" (ByVal hDC, ByVal hObject) SaveScaleMode = Form1.ScaleMode Form1.ScaleMode = 3 'Set to Pixel nIndex = 10 'OEM-dependent fixed font hObject = GetStockObject(nIndex) SavedObject = SelectObject(Form1.hDC, hObject) For i = 0 to 4 For j = 1 to 51 Print Chr$(j*51+i); Next Print Next Form1.ScaleMode = SavedScaleMode I put these instructions into Form1's Click event procedure for testing. If you want to use them elsewhere, you'll need the hDC% for that item. (Blake Ragsdell, Inside Visual Basic) --------------- Framing Controls (the NEXT Look): The following routine adds shading around your controls that gives the look of depth to them. Put this routine in a Module and you can call it from any Form. Note that this routine puts the shadow on the top and left edges of the "recessed box". This is done to match the lighting pattern on the VB buttons. Also, the bottom and right edges are drawn first so that the dark edges will be drawn over the light edges at the corners to create a natural shadow effect. DefInt A-Z Sub Frame (C As Control) Offset = 20 'try experimenting with this value and the next Screen.ActiveControl.DrawWidth = 3 Screen.ActiveControl.forecolor = &HE0E0E0 'you may have to try different color combo's 'bottom: Screen.ActiveControl.Line (C.Left, C.Top + C.Height + Offset) -(C.Left + C.Width, C.Top + C.Height + Offset) 'right: Screen.ActiveControl.Line (C.Left + C.Width + Offset, C.Top) -(C.Left +C.Width +Offset, C.Top +C.Height +Offset) Screen.ActiveControl.forecolor = &H808080 'top: Screen.ActiveControl.Line (C.Left - Offset * 1.5, C.Top - Offset * 1.5) -(C.Left +C.Width +Offset *1.5, C.Top -Offset *1.5) 'left: Screen.ActiveControl.Line (C.Left - Offset * 1.5, C.Top - Offset * 1.5) -(C.Left - Offset * 1.5, C.Top + C.Height + Offset) End Sub (In Form1, for example:) Sub Form_Paint Call Frame(Label1) Call Frame(Text1) Call Frame(List1) Call Frame(Drive1) Call Frame(Dir1) Call Frame(File1) (etc) End Sub Bonus tip: If you have a row of buttons, put them inside a Picture box. (You must draw the button inside the box. You cannot double-click or drag the button into the Picture box.) Set the Picture box's BackColor to the same as the Forms and the Border to none. Now use the routine above to put a frame around the Picture box to make it look like the buttons are in a recessed panel. --------------- "Painting" Not Finishing: Changes to forms, etc, may not show up as soon as you might wish if the program is involved in heavy processing. To get the changes to show up on the screen right away, use .REFRESH, such as List1.Refresh. --------------- Resolution, Determining: The following code returns the dots-per-inch resolution of the display: Global (all on one line): Declare Function GetDeviceCaps Lib "GDI" (ByVal hDC As Integer, ByVal nIndex As Integer) As Integer Form1_Load: xDPI% = GetDeviceCaps(Form1.hDC, 88) This is suffficient for VGA, S-VGA and 8514-A because those resolutions have "Square" screen metrics. I.E. 96X96 DPI or 120X120 DPI. CGA and EGA, on the other hand, use metrics which are not square...CGA is 96X48, EGA is 96X72. If you want to appropriately scale for these resolutions you will need to determine the yDPI as well... yDPI% = GetDeviceCaps(Form1.hDC, 90) (Bob Scherer) --------------- Screen/Window Capture to ClipBoard: PrtSc captures the whole Windows 3 screen to the clipboard. Alt-PrtSc captures the contents of the active window to the clipboard. For better screen grey scales, load the monochrome display driver first. (Jonathan Zuck) The shareware program Paint Shop Pro is an easy way to save captured screens to disk in a wide variety of graphics formats. It also has many options for modifying the image before saving it. From PsL, order disk #3388. You can programmatically send a form to the clipboard as follows: Type RECT left As Integer top As Integer right As Integer bottom As Integer End Type DefInt A-Z Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect As RECT) Declare Sub DeleteDC Lib "GDI" (ByVal hDC) Declare Sub BitBlt Lib "GDI" (ByVal DestDC, ByVal x, ByVal y, ByVal BWidth, ByVal BHeight, ByVal SourceDC, ByVal x, ByVal y, ByVal Constant&) Declare Function CreateDC Lib "GDI" (ByVal Driver$, ByVal Dev&, ByVal O&, ByVal Init&) Dim lpRect As RECT Sub Form_Click () Call GetWindowRect(hWnd, lpRect) DC = CreateDC("DISPLAY", 0, 0, 0) BitBlt hDC, 0, 0, lpRect.right - lpRect.left, lpRect.bottom - lpRect.top, DC, lpRect.left, lpRect.top, &HCC0020 DeleteDC DC Clipboard.SetData (Image) Cls End Sub (Jeff Simms) --------------- Screen Scaling/Form Resizing: You're on your own in terms of rescaling your forms and controls for different resolution screens. Your best bet is to either design exclusively for 640x480 screens (though there are people with 640x350 EGA screens), or to have your program use the Screen.ScaleWidth/ScaleHeight values and adjust your forms appropriately by using percentages of the screen for sizing instead of an absolute number of Twips. All of the VB scale modes use that wonderful and totally baffling item, the "logical inch." A logical inch is defined as an inch as written on paper and not on the screen. Thus, all VB scale modes are relative to how big it prints and not the video resolution of the screen. As such, twips are not "Physical" units of measure with video output, but relative units. This is why forms change relative size as you change video resolution. As an example, a line of text which is 1" long when PRINTED is 72 points or 1440 twips. In std vga mode, the screen width is 7680 twips while in 1024x768 hi-res, the screen width is 12,288 twips (Assuming you use the same system fonts). In VGA, this 1" line of text takes 19% of the screen width to display and in 1024, it only takes 12% of the screen width. Thus, this line of text will physically change relative size between video resolutions while still being 1" long when printed. More Input: The physical size of the monitor isn't what's important. It's the actual pixel resolution of the monitor that determines how a form or control displays on the monitor. I've got a 5D at work and a 4D at home. When in the same resolution, a form occupies the same relative area of the screen on both. It's when I change resolution from 1024 to 640 that big changes occur. As for twips, they are perfectly consistent -- but on the printer and not the screen. Any object 1440 twips wide will print exactly 1" wide. How wide 1440 twips is on the screen changes with video resolution. More: Think in percentages, rather than in terms of absolute measurements. No matter what your screen resolution, you can simply make each control's position and size a percentage of Screen.Width and Height. Form1.Left = Screen.Width * .25 Form1.Top = Screen.Height * .1 Form1.Width = Screen.Width * .5 Form1.Height = Screen.Height *.5 That's the basic structure of the thing. Font scaling and other items can also use the same concept, and your app will look the same no matter what resolution your user chooses. (George Campbell) Also see the ICONWRKS code (comes with VB) for illustrative code. --------------- Screen Scaling Tips: Here are two pointers if you want to be able to Resize your forms: One: Group your controls onto frames or pictures. It is vastly simpler to hide, show and move things around if they're in a common control like a frame or picture box. When I resize a form, I have routines to move the two or three 'container' controls; I then call separate subs to resize/reposition the CONTENTS of each of those controls if required. It sounds like more code, but it actually ends up taking less code and being faster. You do pay a slight memory cost with the added control, but the speed increase is worth it. (2) To avoid recursion, use a flag in the Resize routine: Sub Form_Resize If AlreadySizing Then Exit Sub AlreadySizing = -1 'Code to move controls, etc. goes here. AlreadySizing = 0 Exit Sub Set the AlreadySizing flag anytime you want to make adjustments to the form size without calling the control-moving routines. (Barry Seymour) --------------- Screen Type: z = Screen.Height If z = 6000 Then Type$="CGA" ElseIf z = 7000 Then Type$ = "EGA" ElseIf z > 7000 Then Type$ = "VGA or Better" End if There's another way to do this, calling GetDeviceCaps to find out the vertical resolution; but the above method is a lot easier... BTW, if you want to know if it is exactly VGA, not "or better" (i.e., better than 640x480), the number for that 7200 if memory serves... --------------- Screen Wipes: You can "draw a curtain" over the screen as follows: (Don Funk) CONST RESOLUTION = 768 '1024 X 768 in this example For i = 1 to RESOLUTION LINE (i,1)-(i,1024) Next i --------------- Stop Flashing During Updates: Set an object's Visible property to 0 during updates to speed up the process and to stop the screen from flashing caused by each update being written to the screen. (Set it back to -1 when the update is done, of course.) --------------- Suppress Arrow Cursor: The problem with creating a custom mouse pointer is that VB constantly resets it to the arrow shape. The following routine overcomes that problem. (Gregg Morris) DefInt A-Z Declare Function ShowCursor Lib "User" (ByVal bShow) Declare Function SetCursor Lib "User" (ByVal hCursor) Declare Function LoadCursor Lib "User" (ByVal hInstance, ByVal lpCursorName As Any) Declare Function GetClassWord Lib "User" (ByVal hWnd, ByVal nIndex) Declare Function SetClassWord Lib "User" (ByVal hWnd, ByVal nIndex, ByVal nNewWord) Global Const IDC_CROSS = 32515& Global Const IDC_WAIT = 32514& Global Const IDC_ICON = 32641& Global Const GCW_HCURSOR = (-12) Global Const IDC_ARROW = 32512& Sub Text1_LostFocus () hCursor = LoadCursor(0&, IDC_ICON) h1 = SetCursor(hCursor) h2 = ShowCursor(1) oldcur = GetClassWord(Form1.hWnd, GCW_HCURSOR) newcur = SetClassWord(Form1.hWnd, GCW_HCURSOR, hCursor) End Sub Sub Text2_LostFocus () hCursor1 = LoadCursor(0&, IDC_ARROW) h2 = SetCursor(hCursor) h2 = ShowCursor(1) oldcur1 = GetClassWord(Form1.hWnd, GCW_HCURSOR) newcur1 = SetClassWord(Form1.hWnd, GCW_HCURSOR, hCursor1) End Sub The limitation of this approach is that when you set the class cursor you are changing the cursors for every window in that class. Thus changing the cursor for one text box promptly changes it for every text box. (Daniel Appleman) --------------- Testing for 256-Color Display: You need to call GetDeviceCaps to check the RASTERCAPS value and the bit within it known as RC_PALETTE. In order for a vid card to display 256 or more colors it needs to support palettes, which is what this bit will reflect. I'll leave it to you to figure out the API call. (Steve Cramp) --------------- Twips to Pixels: Here is a routine for converting twips to pixel. Form1 Declarations: Const PIXEL = 3 Const TWIP = 1 Sub Form_Click () Print "Form Left = "; Twips_To_Pixel(Left) End Sub Function Twips_To_Pixel& (ByVal N&) ScaleMode = TWIP Scale_Twip = ScaleWidth ScaleMode = PIXEL Scale_Pixel = ScaleWidth Twips_To_Pixel = Scale_Pixel / Scale_Twip * Left End Function --------------- Windows is Device Independent - NOT!: A form that you carefully lay out on your monitor can look substantially different on other systems' video. John Socha has a full article about this in the July 1992 issue of WinTech magazine, so I don't think the problem can be fully treated here in VB-Tips. The tips in this section can help, but the best bet is to test your program on a wide variety of monitors and card types. --------------- Breakpoints Change Processing: Putting a breakpoint ("Stop") in your code can change the way your application executes. For example, when you press a key while your app is running, the processing normally goes to KeyDown and then to KeyPress. If you put a Stop in KeyDown or if you single-step through KeyDown, processing will not continue on to KeyPress. The same type of thing happens in other situations where a breakpoint has been added. --------------- Code Should Go In Modules: In general, code in modules is less of drag on performance than code in forms; therefore, you should consider putting as much code as possible into modules. (Robert Eineigl, Microsoft Product Support) (1) Module code is more efficient in terms of memory usage and is initialized only one time (i.e., when it is first loaded--although local module variables will be initialized each time the function is called). (2) Form code is not unloaded, but is apparently initialized each time the form associated with it is reloaded (a little paradox there). (3) Static variables are really static, no matter where they appear. (4) It is generally better to put non-event functions into modules. (5) It is efficient to unload forms (actually all that really gets unloaded is the display component of the form, which is all that really needs to be unloaded -- more or less). (6) It is OK to put DLL defines in any module and the effect is the same as putting them in the global module. (J.D. Evans, Jr.) Variables Dim-ed at the general declarations level persist but variables just Dim-ed (as opposed to Static) in a sub are stack variables and thus gone if the form is unloaded. (Robert Eineigl) --------------- Code Window Font Size Changing: It *is* possible to change the size of the font in the code window. You could use a smaller font to see more of your code, for example. In your SYSTEM.INI file, there is a line "FIXED FONT=". You can assign any non-TrueType, non-proportional font name (that's on your hard disk, of course) to this line and it will be used in the VB Code windows. NOTE: Do *not* use a TrueType font because Windows will not even load. Do *not* use a proportional font because the cursor will get lost. (Nelson Ford & Robert Eineigl) --------------- Copying Forms: The easiest way to copy a form to another project (or to duplicate a form in the same project) is to do a Save-As from the original project to give the form a new name, then load it into the (new) project. (Ian Taylor) --------------- Dr. Watson Disaster: Ian Taylor reports that his system was trashed thanks to Dr. Watson: The problem was caused by the Dr Watson Win.Ini switch ShowInfo=par. I had the line: [Dr. Watson] ShowInfo=dis err loc mod par sou in my Win.Ini file. --------------- .EXE Size / Junk In EXE's: 1. Long variable names increase EXE size. 2. Comments add a couple of bytes per each comment line to EXE size. 3. A Global DIM of an array adds to EXE size. For example: Global Sample as String*20000 will add about 20k to the size of the EXE file, but the same DIM in a Form will not. 4. VB "accumulates" variable names and other junk that it does not let go of when you delete and save. This can make your EXE file get larger with each compilation. Here is how to solve the problem (repeat for each form): a. Select "Code" on the VB menu. b. Select "Save Text" and save. c. Select "Load Text" and click the Replace button, then load what you just saved. To get the maximum size reduction, exit and restart VB before step c. Note that to Load the text back in, you must be viewing the code window to which you wish to load the code. Microsoft's Explanation: VB is holding on to all procedure names, variables/consts, control and form names; in short everything that potentially would be needed to evaluate interactive changes in break mode. No question that some mechanism should be in place to purge this before compile. However, consider the case where a compile occurs with no save on existing changes (some would argue that you should be asked or forced to save changes before compile as PDS does): The temp files that VB uses may contain code and references that, though now commented out, are part of the threaded p-code. Rather than dump this and force rethreading of everything, it was decided to hold on to all "threads". The environment stores all this data from every project loaded in a session so if a larger project was loaded prior to the one you're compiling, this hangover gets incorporated into the .exe. I have heard of situations where the old formname (which matched a presently existing one) caused problems when both forms where running in two different exe's. Doing the Save/Load text and starting your final compile from a clean environment should eliminate this. The formname is one of the critical tags that the DLL uses to feed itself so in rare circumstances, to apps with Form1 formnames could interact. All of these issues are known and will be addressed in the next version. Robert Eineigl Microsoft Product Support --------------- Help Compiler Versions: You can download the 3.1 version from the WINSDK Forum, Lib 16. That's also the section where it's supported. There are two "current" versions. HC31.ZIP is the sandard compiler, and HCP.ZIP has the extended compiler; it uses extended memory to allow bigger files but is slower than HC31. (Steve Pruitt) --------------- Printing Out One Sub: The following code makes an applet that can be used to print the code for one Sub: Fm: Jeff Simms 72200,3173 Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Integer Declare Function SetActiveWindow Lib "User" (ByVal hWnd As Integer) As Integer Sub Main () Wnd% = FindWindow("OEBDebug", 0&) i% = SetActiveWindow(Wnd%) x% = DoEvents() SendKeys "^{HOME}^+{END}^{INSERT}{HOME}" x% = DoEvents() x% = DoEvents() ' Throw in more DoEvents if needed SubText$ = Clipboard.GetText(1) Printer.Print SubText$ Printer.EndDoc End End Sub Once you compile this to an EXE, you can add it to the VB menu bar with the VBLAUNCH program. (See Downloadable Files.) (Jeff Simms) --------------- Removing Deleted Control's Code: When you delete a control, the code goes into the General Procedures list. To get rid of it, highlight the entire block of code, including the Sub and End Sub lines, and press Delete. --------------- Saving Your Work: Several users have reported losing their work in different ways. If you always save before test-running your code, you can prevent most losses. If you get any kind of warnings or other indications that something might be wrong with your system, try to get into DOS (or use File Manager) and copy your project files to a backup directory. Many people have had their project files "trashed" when the system went screwy. Another good idea is to save the code to a Text file (under the Code menu) to protect against your FRM file being trashed. There is a utility on the DLs here for copying all the files in a Project - very handy to have, although a better idea, generally, is to keep each project in its own subdirectory, then you can just copy the entire subdirectory to another one. --------------- Splitting Long Lines: VB does not have a line continuation character as does QB. The only way to split a long line of code into multiple lines is with kludges. Say that you have a long line like: If (a=b and not (c=d or e=f) and g=h) or (i=j and k=l) then Call SomeRoutine End if With descriptive variable names, the line above would be long, as well as being difficult to read and follow the logic of. So I would split it up: If c=d then 'do nothing; this just gets rid of the "not (c=d or e=f)" elseif e=f then 'do nothing elseif a=b then Call SomeRoutine elseif i=j then if k=l then Call SomeRoutine end if End If Note that an "or" in a line can be split into an ElseIf while an "and" must be split into an indented "If". --------------- App's hInstance, Getting: Declare Function GetWindowWord Lib "User" (ByVal hWnd As Integer, ByVal nIndex As Integer) As Integer Const GWW_HINSTANCE = (-6) hInstance = GetWindowWord(Form1.hWnd, GWW_HINSTANCE) (Costas Kitsos) --------------- Alias Use in Declares: With the Alias keyword added to Declares, duplicate definitions become apparent immediately when the program is run. Otherwise, there is no indication that DLL's have been declared in multiple places. (Steve Schmidt) --------------- API vs VB Routines: Given the choice, it's preferable to use VB routines rather than API calls: 1. API calls will probably be slower (goes through a p-code declaration). 2. API calls require hWnds for everything. 3. API calls will not be portable accross different OS. - J. Zuck. --------------- API Errors: Errors in WINAPI.TXT can be found in article Q74526 in the Knowledge Base (GO MSKB on CompuServe). --------------- Autoredraw Error Message: "Can't create autoredraw image!" means you're out of ram! Virtual (disk) memory won't do for creating the persistent bitmap (which is what autoredraw is trying to do). You need to unload some hoggish forms or toggle autoredraw on and off instead of having it on for several objects that all need persistent bitmaps created concurrently. (Robert Eineigl) --------------- Blanking Out Text: You cannot blank out text on the form or picture box by writing blank spaces over it as you can in other BASICs. Instead, set the FontTransparent to FALSE and you'll be able to overwrite the text that's already there with spaces. Note that spaces aren't as wide as most characters (in proportional fonts), so you'll have to use more spaces than you might use under a DOS Basic. (Ted Young) --------------- Branding Your Apps: To brand your application with the user's name, address, whatever, set a variable in your app to an identifiable string, such as "ZZZZZZZZZZ". During runtime, you can substitute the users's name for the string by opening your EXE as Binary, searching for the string, substituting the name, and writing it to the file: Open MYPROG$ For Binary As #1 A$ = String$(LOF(1), Chr$(32)) Get #1, , A$ If wDlg = 0 Then x& = InStr(A$, DummyString$) UName$ = String$(30, Chr$(90)) Else x& = InStr(A$, String$(30, Chr$(90))) End If Seek #1, x& Put #1, , UserName$ Close #1 If you want to be able to continually replace the string in this location, then you will need to leave part of the String the same, say the first 5 'Z''s. (Troy G. Strain) --------------- Control ID's: CTLHWND.DLL lets you get a control's handle without having to use the clumsy SetFocus method. But its use in your app requires you to distribute yet another DLL, which is not desirable either. If you wanted to do without CTLHWND.DLL but still benefit from it, you can use it to get your control's IDs during development. Then right before making the EXE, save the control IDs in global constants and remove CTLHWND.DLL from the project (the IDs won't change): DefInt A-Z Declare Function GetDlgCtrlID Lib "User" (ByVal hWnd) Declare Function ControlhWnd Lib "CTLHWND.DLL" (C As Control) Dim ID_COMMAND1 Dim ID_COMMAND2 Sub Form_Load ID_COMMAND1 = GetDlgCtrlID(ControlhWnd(Command1)) ID_COMMAND2 = GetDlgCtrlID(ControlhWnd(Command2)) End Sub Right before making an EXE just replace the two Dim lines with Constants. Once you have the control IDs you only need to call GetDlgItem(Form.hWnd, ID) to get the hWnd. (Costas Kitsos) --------------- Currency Format vs Date Format: From Sharon Dooley: Once you format a currency variable, the DATE functions no longer work correctly. For example: Print Format$(0@,"0.00") Print Year(Now) will display 0 1900 Microsoft has confirmed this bug. The only way around it is to format some non-currency before using the date format: Print Format$(0@,"0.00") Print Year(Now) Print Format$(0%, "###0") Print Year(Now) which prints four lines containing 0, 1900, 0 and 1991, respectively. --------------- DateValue vs International Format: The DATE$ system variable always returns date in mm/dd/yy format, no matter how the Windows International Date stuff is set. DateValue() expects a character string in the format specified by the Windows International Date stuff. Therefore, n#=DateValue(Date$) will fail with INVALID FUNCTION CALL if the International Date format is defined as YY/MM/DD. The workaround is to use: n#=Int(Now) (Ed Tenholder) --------------- DateValue Bug: The following code is supposed to create a serial number from a given date. DV# = DateValue("Oct 14") The VB Reference manual says that if the year is omitted, the current year is used, but if you translate the number back to text with Print Format$(DV#, "mm/dd/yyyy") you get: 10/01/1914 If you use "10-14" instead of "Oct 14", it works. --Isaac Rabinovitch --------------- "DISPLAY.EXE" Won't Run: DISPLAY is a reserved word in Windows. If you name an app "RESERVE", it will not execute. (David Weldon) --------------- DoEvents(), Use Of: DoEvents allows the user to access other programs (or other parts of the current program) while the current app is otherwise tying up the CPU in some sort of processing loop. You should put "x = DoEvents()" in your code anywhere you don't mind having the current operation interrupted. There is a risk that if the user clicks a button that causes lengthy processing and during that time, clicks the same button again, unwanted results could occur. To prevent such happenings, you should set a global flag to indicate that you are already in that Sub, and then check the flag each time the Sub is entered, doing a Sub Exit if it is set. Setting MousePointer to an hourglass at the start of long processing alerts the user and helps prevent unnecessary extra button clicking. --------------- Emulating READ-DATA: VB does not have READ and DATA. You can emulate this function by assigning data to a string and reading it from there: MyData$ = "Item1, Item2, ..., ItemN," ReDim Array$(N) i = 1 Do j = Instr(i, MyData$, ",") Array$(x) = Mid$(MyData$, i, j-i) i = x + 2 Loop while i < Len(MyData$) --------------- Error Messages: When you get an error message while programming or running inside VB, just press F1 before pressing the Ok button and an explanation of the error message will be shown. --------------- Error Trapping: The error trapping in Visual Basic is local to the Sub or Function and you will have to write a general error handler and place a call in every level to this error handler. --------------- Error: "Wrong DLL Version": If you get this error, about all you can do is restart Windows. There is a long explanation of what may be confusing Windows, but the bottom line is that you have to restart. --------------- Far Pointers: Here's a question regarding direct calls to the Windows API from a Visual Basic procedure. In trying to set up a call to the API entry "Polygon", I discovered that one of the arguments is a far pointer to an array of structures. I've searched the VB documentation, but can't find a method that will return the run-time address of a variable (or an array element) as a far pointer. Is there a VB technique for passing a far pointer as an argument -- if so, how? Also, how would such an argument be specified in the corresponding DECLARE statement? Many thanks to anyone who can supply this information. (Fm: Mark Novisoff (TA) 73047,3706) If the structures themselves don't require pointers (which is the case with Polygon), then it's a piece of cake. Use TYPE...END TYPE to declare a model structure, and then DIM an array of the structures. In your Global module Declare statement, use the "As" syntax: Type Points ' Define the structure X As Integer Y As Integer End Type Declare Function Polygon Lib "Gdi" (hDC%, MyStruc As Points, nCount%) In your code: ReDim MyStrucArray(0 To 10) As Points ' Set the variables in the array here Result = Polygon(hDC%, MyStrucArray(0), nCount%) Note that this results in passing a far pointer to the zeroth element of the array. Because the length of the structure is known to the DLL routine, it will figure out where the rest of the elements are. --------------- Help Files: 1. You can create Help files more easily and cheaply than with the SDK: use the help compiler (HC.EXE) that comes with Turbo Pascal for Windows and Borland C++. 2. A shareware program named Xantippe is a good front-end for making help files. 3. Iconwrks, which comes with VB, contains a Sub called Get_Help. This shows how to call the Win API function, WinHelp, to invoke Windows' Help system. 4. WordPerfect's RTF format for footnotes is reported not to be compatible with the Help Compiler. 5. There are several utilities available for generating Help files without having to learn WFW or other RTF-compatible word processor. They range from very expensive to inexpensive shareware. (See the program listing at the end of the file for shareware solutions.) --------------- Interrupting a Routine: Question: I have code attached to a control button called "Start". When you click on it it increments a counter to the screen with a FOR loop. I want this to continue until I click on a control button called "Stop". VB seems to keep control until the loop ends and it does not see the click on the "Stop" button. How can I get around this? Answer: Use a DoEvents (see your manuals for details): Example: While DoEvents and CounterOn Counter = Counter + 1 Wend Where CounterOn is a global variable you set to FALSE from your Stop button. --------------- Julian Date Considerations: RE: Finding out what day of the week July 4th, 1776 was. You need to be a little careful about dates when going back that far. Although the Gregorian Calendar was invented in 1583, it was not universally and immediately accepted. The British didn't adopt it until the second half of the 18th century and the U.S. after that (and not all states at the same time). In addition, the new year was moved from Mar 25 to Jan 1. Geo Washingtons birthday was Feb 11, 1731 O.S. (old style), but was changed to Feb 22, 1732 N.S. (new style). China didn't change until 1912 and Turkey until 1917. The original accumulated error in 1583 was estimated to be 10 days, but this expanded to 11 and then 12 days the longer the country waited to adopt the standard. To change from old style julian dates to modern gregorian dates, add 10 days to dates oct 5, 1582 through feb 28, 1700; 11 days to dates through feb28, 1800; 12 days through feb28, 1900; and 13 days through feb28, 2100. If you want a program to back it out the other way, you'll need to subtract these figures to arrive at the true O.S. date value. (Bruce Fulton) --------------- Jumping to a Subroutine During Coding: If the cursor is on the name of a sub or function call, you can jump to that code simply by pressing Shift-F2. Unfortunately, there is not a key for jumping back to where you were. (Kevin Cook) --------------- Memory, Out of: 1. An "Out of Memory" error when you know you have plenty of memory often means that you are using up too much of the systems resources. This results from using too many controls -- particularly Picture boxes. To cut back on Label boxes, see "An Alternative to Label Boxes" under "Label Boxes" in this file. To cut back on Picture boxes, see "An Alternative to Picture Boxes" under "Picture Boxes" in this file. 2. You can also get this error if your code gets caught in a recursive loop. This usually is the result of a routine calling itself or a routine calling another routine that calls another routine (etc) that calls the first routine. To track down the problem, run your program in the VB environment. When you get the error message, use Alt-R, M to move the Next Statement to Exit Sub and press F8 to single-step. This will take you to the calling sub. Repeat the process of jumping to Exit Sub and see where it takes you. Usually you will see that the program flow keeps going back from Exit Sub to the same line in the same Sub, then fix that line. If it is a more complex recursion, the single-step will flow back to some other Sub, but eventually you can spot where the recursion is coming from. --------------- Memory, Out of - Timing: 1. If I have an On Error handler in one of my routines and this routine loads a form that does not have it's own On Error handler and there is an error in the Form_Load procedure (e.g., Out of Memory), the error is not passed back to the parent routine as one would expect from reading chapter 19 in the VB programmer's guide. Apparently the Invocation path is only relative to current form. 2. If an Out of Memory error occurs Form_Load starts, then the calling routine handles the error. If an Out of Memory error occurs during the Form_Load then the calling routine does not handle the error. (Rick J Andrews) --------------- NEVER USE "END": From Mark Novisoff, Microhelp: We found a *terrible* problem with End just recently (concerns custom controls). Unloading all forms in your apps has the same effect as End and custom controls get the required messages correctly. Jonathan Zuck adds: I found this problem also when working on a DLL for my column. The problem is that End does a FreeLibrary *before* it destroys the application windows (including custom controls). This is particularly a problem when subclassing. What you need to do is in your WEP, clean up *everything* rather than relying on getting the WM_DESTROY message, or whatever, to the particular window. I think you will find similar problems if the application is ended by choosing "End" from the Run menu of the development environment. --------------- Polygon-Drawing API Call: Global Const gCYAN = &HFFFF00 Type PointAPI X As Integer Y As Integer End Type Declare Function Polygon Lib "GDI" (ByVal hDC As Integer, lpPoints As PointAPI, ByVal nCount As Integer) As Integer This code will draw your polygon. Change the number of points to suit your needs. TheForm.FillColor = gCYAN ReDim Points(4) As PointAPI Points(1).X = 100 Points(1).Y = 100 Points(2).X = 200 Points(2).Y = 100 Points(3).X = 200 Points(3).Y = 200 Points(4).X = 100 Points(4).Y = 200 Result% = Polygon(Theform.hDC, Points(1), 4) . (Bob Scherer) --------------- Private INI Problem: If you get bizarre happenings after reading from an INI file (using API calls), the most likely reason is that you have not initialized the variable used to return the data to a sufficient length. For example, if you want to retrieve data of up to 15 characters from an INI file, initialize the variable with something like X$ = Space$(15). (Mark Novisoff) --------------- RND Seeding: If you need to seed the random number generator ("RND") to a specific series of random numbers, such as for being able to duplicate deals in a card game, then you should be aware that: Randomize n! 'where "n!" is some specific value does NOT reseed RND() with n! as the manual implies. (In fact, there is no apparent use for "Randomize n!". Instead, use: x = RND(-n!) (Jim Mack) This feature is downplayed, to say the least, in the VB manual. Also, the VB manual is a little deceptive in showing the syntax as being RND(-n#). While it is true that you can use a double-precision number, the fact is that a number longer than 7 digits will not generate a UNIQUE series of numbers. That is, if you increment an 8+ digit number by one, the subsequent series of numbers may not change from the previous series -- not ideal if you are generating card deals. (Nelson Ford) --------------- Slow Loading of App: If your app takes a long time to load, try the following procedure to keep your users from getting antsy: Create a "Banner" form with only a small label which says "Loading Program" and a Timer Control (Enabled). Make this form Borderless, no Max, Min, or Control Box. Put a Screen.MousePointer = 11 as the only command in the Load Event. Select this as the start-up form. This form will load fast. Under the Timer event, disable the timer, Load MainForm (with Visible = False), and MainForm.Visible = True. The Load Event of MainForm should Unload Banner. The result is that you get a fast mousepointer 11 and a "please wait" message which lasts until the main screen gets loaded. (Al E Meadows) --------------- Sound Standards: Now that Windows 3.1 is here, let's all make an effort to observe standards. Notably the new sounds for MsgBox dialogs: Call MessageBeep before MsgBox dialogs. Follow the chart below.. MsgBox with no icon = 0 MsgBox with Stop icon = 16 MsgBox with Question mark = 32 MsgBox with Exclamation mark = 48 MsgBox with lower-case i = 64 And use the value above for the parameter to pass to MessageBeep. The declare for MessageBeep is... Declare Sub MessageBeep Lib "User" (ByVal wType As Integer) Doing this will make sure that the proper user-assigned sounds are played. (Mike Mezaros) --------------- Telephone Dialing: OPEN "COM1" For Output As #1 Print #1, "ATD 000-0000" Here is a more complete routine from Mark Novisoff. ' General section Dim PortIsOpen As Integer Sub Form_Click () ' Tells me to dial the phone 'get PhoneNumber$ from the form, however you have the user input it. If Not PortIsOpen Then PortIsOpen = -1 Open "COM1" For Output As #1 End If Print #1, "ATDT" + PhoneNumber$ + Chr$(13) End Sub Sub Form_DblClick () ' Tells me to hang up If PortIsOpen Then Print #1, "ATH" + Chr$(13) Close #1 PortIsOpen = 0 End If End Sub --------------- "Too Many Files": If you get the message "Too many files", try the following: General: DEFINT A-Z Declare Function SetHandleCount Lib "Kernel" (ByVal wnumber) Form_Load: j = SetHandleCount(32) The default number of file handles is 20 (actually 15 because the system uses 5). Entering 32 will bump your total number of handles up quite a bit. (Todd W. Bristol) --------------- TRUE <> NOT FALSE: The following is a quirk of all versions of MS Basic, not just VB: (1) Any non-zero number will return a "true" result with the syntax: "If x..." but only "-1" will work with the syntax: "If NOT x Then ... Else...". Example: x = 1 if x then print "x is True" if not x then print "x is False" else print "x is True" x = -1 if x then print "x is True" if not x then print "x is False" else print "x is True" Run this and you get: x is True x is False x is True x is True Here's a typical example of how you can get caught by this: If Not InStr("13579", n$) When i is 5, InStr("5", Str$(i)) will have a value of 3, which as we have just seen, will not trigger a "Not" test correctly. Instead, you must say If InStr("13579", n$) > 0 (2) Here is related quirk: If X and Y are both -1, then the following statement will print "TRUE": If X And Y Then Print "TRUE" This code will also print "TRUE" for some, BUT NOT ALL, positive values of X and Y, such as 2 and 4. (Nelson Ford) --------------- WAV File Playing & Stopping: Declare Sub SndPlaySound Lib "MMSystem.dll" (ByVal WavFile$, ByVal wFlags As Integer) SndPlaySound "tada.wav", 2 (Royce Bacon) To stop the playing of a WAV, create a short, blank WAV file and "play" it. (Mark Burgess) --------------- Windows 3.1 Quirks: 1. A bug in Win31 and/or VB causes your program not to show up when you go to make an EXE in the File menu. If you select the form whose icon you want to use, it will still use it, you just want be able to see it until run-time. 2. The "System" nonproportional 9 point font has been renamed "Fixedsys", which could cause problems. --------------- Calculating System Resources: This is an *UNDOCUMENTED* API call which does the trick: Declare Function GetModuleHandle Lib "Kernel" (ByVal lpModuleName As String) As Integer Declare Function GetHeapSpaces Lib "Kernel" (ByVal hModule As Integer) As Long '*UNDOCUMENTED* Function CalcFreeResources (file As String) As Long info& = GetHeapSpaces(GetModuleHandle(file)) CalcFreeResources = (LowWord(info&) / HighWord(info&)) * 100 End Function Function GetSystemResources () gdires = CalcFreeResources("GDI") userres = CalcFreeResources("USER") If gdires < userres then GetSystemResources = gdires Else GetSystemResources = userres End If End Function Function LowWord (dlong As Long) As Long LowWord = dlong And &HFFFF& End Function Function HighWord (dlong As Long) As Long HighWord = (dlong And Not &HFFFF&) / &H10000 And &HFFFF& End Function (Hans Peters) --------------- Clipboard - Adding CR-LF: You can add a blank line in text going to the clipboard, whether or not it is interpreted properly depends on the receiving app. Example: text1.text = "AA" + Chr$(10) + Chr$(13) + "BB" + Chr$(10) + Chr$(13) + "CC" clipboard.SetText text1.text text2.text = clipboard.GetText(1) In Winword and Write, the paste came in formatted correctly. In NotePad, the two characters were printed as unprintable characters (black boxes) since Notepad doesn't recognize these via the clipboard. (Robert Eineigl) --------------- Close Another App From Within VB: Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long Global Const WM_CLOSE = &H10 Assuming hWndApp is the handle, then the usage would be: msg = SendMessage(hWndApp, WM_CLOSE, 0, 0&) (Keith Pleas) Another way to close an already running program is to use AppActivate and SendKeys. AppActivate doesn't return a value, but you can use On Error. In most cases, you can use the control bar and shut it down. All assuming that the program closes gracefully. Oh yeah, you'll want to set the wait argument to -1 in the SendKeys command. (George Campbell) --------------- Control Box Menu, Adding Items: The "Control Box" is the box in the upper left corner of a form. Clicking on it during run-time brings up a "system menu" (so some people call the box a "SysBox"). You can add items to the Control Box menu, but you can't attach any code to it, normally (since VB won't process user-defined WM_SYSCOMMAND events). However, you can INDIRECTLY trigger user-specified events by using the SysBox CLOSE event (Form_UnLoad) to replace the SysBox Menu with a PopUp Menu of your own from which the user can select an action. The beauty of this method is that you can use the VB Menu Design Window to build your PopUp menu. This simplifies the work as the VB MDW automatically maps menu events to procedures. The code is as follows: Form_Load: 'Use API to get handle to Window Menu Bar (created with VB Menu Design) hWndMenu% = GetMenu(hWnd) 'Use API to get handle to 1st (and only) submenu on Menu Bar hPopUpMenu% = GetSubMenu(hWndMenu, 0) 'Use API to get name of submenu which is to replace Control Box 'Close' '(MStg$ is name of submenu as it appears on your menu bar) nchar% = GetMenuString(hWndMenu, 0, MStg$, Len(MStg$), MF_BYPOSITION) 'Use API to modify Control Box (System) Menu to replace 'Close' 'with your submenu name hSysMenu% = GetSystemMenu(hWnd, FALSE) boolean% = ModifyMenu(hSysMenu%, SC_CLOSE, MF_BYCOMMAND, SC_CLOSE, MStg$) 'Use API to Remove Menu Bar from Window (this is not an option!) If RemoveMenu(hWndMenu, 0, MF_BYPOSITION) Then DrawMenuBar (hWnd) Form_Unload: 'If UnLoad event is not thru this Window's Control Box Menu then Exit: If SetActiveWindow (hWnd) <> hWnd Then End 'Otherwise, Replace Control Box Menu with own PopUp Menu '(PopUpPosition is declared as 'Dim PopUpPosition As POINTAPI') PopUpPosition.x = Screen.ActiveForm.Left 'Position in Twips PopUpPosition.y = Screen.ActiveForm.Top 'Convert twip position of form to pixel position '(TwipsToPixels is Sub of my own design to do this.) Call TwipsToPixels(PopUpPosition, PopUpPosition) 'Position PopUp Menu where Control Box is X% = PopUpPosition.x 'Position in Pixels Y% = PopUpPosition.y + GetSystemMetrics(SM_CYSIZE) Cancel = TRUE '(Don't forget this! Or your program will exit) boolean% = TrackPopupMenu(hPopUpMenu, 0, X%, Y%, 0, hWnd, 0) Since you are replacing the "Close" function of the Control Box, you need to make "Close" one of the options in your PopUp submenu. Also, since the Menu Bar will be deleted as a part of this method, your form will adjust itself vertically to replace the missing menu bar. If you set AutoSize=True for the form, this is taken care of, otherwise you need to keep this in mind when fixing the form borders. You must add the necessary DECLARES for the the API functions and constants used in the code. They can be found in WINAPI.TXT. This technique can also be used to create a 'floating' PopUp Menu just about anywhere. (Gary Williamson) --------------- Control Box Menu, Removing Items: Defint A-Z Declare Function RemoveMenu Lib "User" (ByVal hMenu, ByVal nPosition, ByVal wFlags) Declare Function GetSystemMenu Lib "User" (ByVal hwnd, ByVal bRevert) Global Const MF_BYPOSITION = &H400 Form_Load: HSysMenu = GetSystemMenu(A_Form.Hwnd, 0) R = RemoveMenu(HSysMenu, 7, MF_BYPOSITION) 'Separator R = RemoveMenu(HSysMenu, 6, MF_BYPOSITION) 'Close End Sub --------------- Control Box Exit Bug: If you exit a compiled VB app by using Exit from the form's Control Box menu, the program may not close down properly and may not free up system resources. If there is more than one form in the project and there is no code in that forms Unload procedure to unload all other forms or END then the DLL stays loaded and the app is still a task so no resources are freed (other than those associated with the one unloaded form). However, if there is code in the unload (to trigger the exit routine or END) it will be executed by the System menu close and has the same effect as triggering your exit routine with a button. If there are loaded forms orphaned, ending the task from the Task Manager frees the resources. One of the resources not freed up in some situations is VBRUN100.DLL, which Windows does not remove from memory. If you run a VB app from drive A: with VBRUN on A: and the forms are not properly unloaded as described above, then Windows will continue to look for VBRUN on A: and display an error window if it is not still there. However, canceling the error window allows the next invocation of a VB app to look for VBRUN on your hard disk again. (Robert Eineigl) --------------- Control Accessing Via DDE: A control on a frame cannot be accessed via DDE. I had tested this with three applications, WinWord, Superbase and Objectvision. The latter two produce an immediate, null result to a DDE request; WinWord seems to recognize the control is there and keeps trying to get the data until it eventually times out. (George Mair) --------------- Controlling Apps with SendKeys: It is easy to provide buttons for menu items in other apps using the VB 'SendKeys' command. For example, attach the following code to a pushbutton to cause the Program Manager group windows to be cascaded (using the Window/Cascade menu option): Sub Command1_Click () AppActivate "Program Manager" ' Gives focus to the program manager SendKeys "%Wc" ' Sends ALT+W c to progman End Sub (Danny Dawes) --------------- Corrupt VB File Recovery: If you get a UAE and have to reboot in the middle of running an app in VB, you may get an error message when trying to reload your source code, meaning that it has become corrupted. The only option at this point, if you do not have backup, is to check for any .TMP files left in your Window's TEMP directory and delete them prior to loading Windows/VB/project again. A good habit before starting a programming session is to archive (using LHA, PKZIP, etc) your project files, using the day's date for a name. That way you do not risk losing everything. Another good habit is to save your code as Text (under the Code menu) before ever running the project in the VB environment. Although this will not safeguard the forms and controls, the code is normally a lot more work. --------------- DDE Message Limit: There is an 8 message limit to the DDE message queue per task. This might occur due to a surge in update messages for hot links simultaneously or simply due to the total number of DDE messages hitting the app's queue. Try instituting an error trap (this is a trappable VB error) and logging the time the trap is triggered to see if doing a "resume next" in the trap will hurt the program. (Robert Eineigl, Microsoft Product Support) --------------- Detecting Running Applications: 1. To determine if an application is already running, ask Windows to find its title caption. Use this code: DefInt A-Z Declare Function FindWindow Lib "user" (ByVal CName As Any, [all on one line]---> ByVal Caption As Any) Function Loaded (Caption$) Loaded = FindWindow(0&, Caption$) 'use call If Loaded > 0 Then Loaded = -1 'if caption found End Function Example: If NOT Loaded("Program Manager") Then dummy = Shell("PROGMAN.EXE",1) End If 2. Another way to detect if an application is already running: Declare Function GetModuleHandle% Lib "Kernel" (ByVal lpProgramName$) Declare Function GetModuleUsage% Lib "Kernel" (ByVal hProgram%) Sub Form_Load () hw% = GetModuleHandle("project.EXE") If GetModuleUsage(hw%) > 1 Then MsgBox "This program is already loaded!", 16 End End If End Sub This makes one assumption that may bite you: it assumes there is and will be only one instance of the module running. Since there is no way to get an instance handle from a module handle, there is no way to track multiple instances this way. (Robert Eineigl) ------------- Detecting the Start-Up Directory: DefInt A-Z Declare Function GetWindowsDirectory Lib "kernel" (ByVal P$, ByVal S) Declare Function GetModuleHandle Lib "kernel" (ByVal FileName$) Declare Function GetModuleFileName Lib "kernel" (ByVal hModule, ByVal FilName$, ByVal nSize) Dim hMod, path As String hMod = GetModuleHandle("MyApp.EXE") path = String$(145, Chr$(0)) path = Left$(path, GetModuleFileName(hMod, path, Len(path))) VbPos = InStr(path, "\MyApp.EXE") If VbPos = 0 Then Dim WinPath As String WinPath = String$(145, Chr$(0)) Response = MsgBox("MyApp.EXE is Missing or has been renamed", 48, "Will use default path") Label1.Caption = Left$(WinPath, GetWindowsDirectory(WinPath, Len(WinPath))) + "\MyApp" Else Label1.Caption = Left$(path, (VbPos% - 1)) End If (Arthur Edstrom) --------------- Drive Type Detection: To detect what type of drive you're dealing with: DefInt A-Z Declare Function GetDriveType Lib "Kernel" (ByVal nDrive) Global Const DRIVE_REMOVEABLE = 2 Global Const DRIVE_FIXED = 3 Global Const DRIVE-REMOTE = 4 Answer = GetDriveType(DriveNum) ' Drive A: is 0, B: is 1, C: is 2, and so forth. Select Case Answer Case DRIVE_REMOVEABLE Whatever Case DRIVE_FIXED Whatever Case DRIVE_REMOTE Whatever Case Else Error handler End Select (John Murdoch) --------------- Exit Windows from VB: 1. Declare Function ExitWindows% Lib "user" (ByVal dwReserved&, ByVal wReturnCode%) Form_Click: RetVal% = ExitWindows(0,0) or: 2. Declare Sub ExitWindows Lib "User" (ByVal dwReturn&, ByVal wReserved) Const EW_RESTARTWINDOWS = &H42& Form_Click: RetVal% = ExitWindows(EW_RESTARTWINDOWS, 0) --------------- Faster Loading of Apps: VB apps will load faster if you load VBRUN100.DLL during Windows startup. You don't even need a form. You can write a program that has nothing but a Sub Main. In that, you will want to call the LoadLibrary API function to load VBRUN100.DLL. This increases the lock count on the DLL so that even when you exit from your app, the DLL remains loaded. This way, your EXE does not need to remain in memory. DefInt A-Z Declare Sub LoadLibrary Lib "Kernel" (ByVal LibName$) Sub Main () LoadLibrary "VBRUN100.DLL" End End Sub That's all there is to it. (Jonathan Zuck) --------------- File Creation Delay: If you create a file, eg: for temporary use, and immediately try to use it, it may not show in the directory. This problem is due to Windows' handling of file buffers. A timer that checked for the file's availability using an error trap is one way to overcome this problem. -Robert Eineigl --------------- File Handles - Maximum: Windows Allows a maximum of 255 file handles system wide. It does not matter what you have specified in the Config.sys "FILES=". The "SetHandleCount" SDK function is the correct way to increase an app's default number of 20. Windows allocates a default of 20 to any task, or app spawned by it (Windows 3.0 is a single task with multiple app's). A task (generally a DOS task) is time sliced in 386 enhanced mode according to the parameters set for forground and background execution. To increase the "TASK" default number set the "PerVMFiles= " parameter in your SYSTEM.INI file. Be aware that the 255 available handles are shared by all tasks and apps. When you grab a set of handles, they are removed from the available pool. It would be a good idea to set the handle count back to the default after you are through with them. Remember, the return value from the SetHandleCount function is the number actually allocated. Also, Windows itself and Visual Basic may have 4 or 5 files open (like win.ini or GLOBAL.BAS). (Tony Altwies) --------------- Finding Excel: The parameters that acknowledge Excel's presence are: classname$ = "XLMAIN" + chr$(0) mywinname$ = "Microsoft Excel"+chr$(0) findwindow(classname$,mywinname$) The parameters that will work for finding a specific excel window are still not settled, but something like: classname$ = "EXCEL5" + chr$(0) mywinname$ = "DDE1.XLS" + chr$(0) --------------- Linking to Excel: Sharon Dooley 70740,2330 This fragment shows what I've done to send stuff to Excel, let the user play around, and then come back to my program. Global: DefInt A-Z Declare Function GetActiveWindow Lib "User" () Declare Function ShowWindow Lib "user" (ByVal hWnd, ByVal nCmdShow) Declare Function PutFocus Lib "user" Alias "SetFocus" (ByVal hWnd) Declare Function IsWindow Lib "User" (ByVal hWnd) Form_Load: ThisApp = StartApp(ToExcel.ExcelBox, "Excel", CurrPath + "expenses.xls", "excel c:\timelog\expenses.xls", 2, hActive) BuildLink ToExcel.ExcelBox, ThisApp, CurrPath + "expenses.xls" '.... here I send my data to Excel ' DestroyLink ToExcel.ExcelBox ' transfer control to Excel and wait till ' the user exits Dummy = ShowWindow(hActive, SW_SHOWNORMAL) ' and restore its size Dummy = PutFocus(hActive) ' give it the system focus Do While IsWindow(hActive) x% = DoEvents() Loop ' when user exits Excel, control returns here MyForm.Show Function StartApp (Link As Control, AppName As String, Topic as String, CommandLine As String, Style As Integer, Hwnd As Integer) As String On Error GoTo StartUp Link.LinkMode = NONE Link.LinkTopic = AppName + "|" + Topic Link.LinkMode = COLD Exit Function StartUp: If Err = DDE_NO_APP Then T% = Shell(CommandLine, Style) StartApp = "Excel" + LTrim$(Str$(T%)) Hwnd = GetActiveWindow() Resume Else MsgBox "UnKnown Error " + Str$(Err) + " in StartApp", \ MB_ICONSTOP, "Timelog Error" Status = VBPXExit() Stop End If End Function --------------- Get Program's Path & EXE Name: The following routine will get the full path and filename for your program. (NOTE: This will not work if the user has renamed the EXE file!) DefInt A-Z Declare Function GetClassWord Lib "user" (ByVal HWnd, ByVal nIndex) Declare Function GetModuleFileName Lib "kernel" (ByVal HWnd, ByVal Fi as String, ByVal FiLen) Sub Form_Click () Const GCW_HMODULE = (-16) Filename$ = Space$(127) hModule = GetClassWord(Form1.hWnd, GCW_HMODULE) 'substitute the form name FLength = GetModuleFileName(hModule, Filename$, 127) Print Left$(Filename$, FLength) End Sub --------------- Get System Focus: VB provides no way of detecting when your app has been given the system focus. There are a number of Windows messages that the form does not translate into events for you. For example, you have no way to prevent an iconized program form being opened but a C programmer does. Also, you have no way of knowing if a form was moved but again, this is a message sent to a form. (Jonathan Zuck) Here is a routine for detecting when an app gets/loses system focus (Don Funk): Global.Bas: DefInt A-Z Declare Function GetActiveWindow Lib "User" () Global Focus Sub Timer1_Timer () If Focus Then If GetActiveWindow() <> Form1.hWND Then Print "Lost Focus" Focus = 0 End If Else If GetActiveWindow() = Form1.hWND Then Print "Got Focus" Focus = -1 End If End If End Sub Sub Form_Load () Focus = -1 Timer1.Interval = 10 End Sub --------------- hWnd for a Control: All you have to do is use the Windows API GetFocus call after setting the focus to the desired control: General Section Declare Function GetFocus% Lib "User" () In your code Command1.SetFocus ButtonHwnd = GetFocus() --------------- hWnd for a Control II: Fm: Costas Kitsos 73667,1755 Here's yet another way of getting a Control's hWnd without having to use that ugly SetFocus method . It will also get hWnds for Labels. If you use ClipSib to create overlapping controls, this will not work. Declare Function ChildWindowFromPoint Lib "User" (ByVal hWnd As Integer, ByVal dwPoint As Long) As Integer Function GetHwndCtl (FrmhWnd As Integer, Ctl As Control) As Integer OldMode = Scalemode Scalemode = 3 GetHwndCtl = ChildWindowFromPoint(FrmhWnd, (Ctl.top * &H10000) + Ctl.left) Scalemode = OldMode End Function --------------- .INI Files: The calls to read/write private ini file are: 'strings: Declare Function GetPrivateProfileString Lib "Kernel" (ByVal Appname As String, ByVal KeyName As String, ByVal DEFAULT As String, ByVal ReturnedString As String, ByVal MaxSize, ByVal FileName As String) Declare Function WritePrivateProfileString Lib "Kernel" (ByVal Appname As String, ByVal KeyName As String, ByVal NewString As Any, ByVal FileName As String) Example: 'saving form's screen position: x = WritePrivateProfileString("Position", "Top", Str$(Form1.Top), "MyProg.INI") x = WritePrivateProfileString("Position", "Left", Str$(Form1.Left), "MyProg.INI") 'reading form's screen position: x$ = Space$(6) 'note that it is critical that the return variable be initialized to a ' to enough space to hold the data to be returned. r = GetPrivateProfileString("Position", "Top", "0", x$, 6, "CSHearts.INI") If Val(x$) > 0 Then Form1.Top = Val(x$) r = GetPrivateProfileString("Position", "Left", "0", x$, 6, "CSHearts.INI") If Val(x$) > 0 Then Form1.Left = Val(x$) The declares and calls to read/write the WIN.INI file are almost the same: GetProfileString(AppName$, KeyName$, Default$, Buffer$, Size%) (Nelson Ford) To delete a line from an INI file, make the third variable in the Write statement &0, as in: x = WritePrivateProfileString("Position", "Top", &0, "MyProg.INI") To blank a variable from a line in an INI file, make the third variable = "": x = WritePrivateProfileString("Position", "Top", "", "MyProg.INI") (George Melcher) --------------- lpCaption$ For an App: Q: Is there a function that returns lpCaption? I am having a bit of a problem with VB in determining how to activate a particular program if it is running. AppActivate wants a title, but how do you determine it? Take calendar, for example. I can determine if it is running with lpClassName$ = "CalWndMain" If FindWindow(lpClassName$, NULL) <> 0 Then ... but how do I then AppActivate lpCaption$ if lpCaption$ changes according to the calendar loaded (defaults to "Calendar - (Untitled)". (For the same reason, I have to use FindWindow with lpClassName rather than lpCaption to determine if Calendar is running.) A: You can get the title using GetWindowText(): lpClassName$ = "CalWndMain" wnd% = FindWindow(lpClassName$, NULL) If wnd% Then text$ = Space$(30) nchars% = GetWindowText(wnd%,text$,30) lpCaption$ = Left$(text$, nchars%) AppActivate lpCaption$ End If Or you can use SetWindowPos() or SetActiveWindow() instead of AppActivate: lpClassName$ = "CalWndMain" wnd% = FindWindow(lpClassName$, NULL) If wnd% Then i% = ShowWindow(Wnd%, SW_RESTORE) ' or SW_SHOW if not minimized End If To check if a window is minimized you use IsIconic(): i% = IsIconic(wnd%) ' if i% is not zero the window is minimized (Jeff Simms) --------------- Mode - Which You Are In: In Windows, click on Help and then "About Program Manager". Box will tell you what mode you're running in. --------------- Multi-Instance App Prevention: 1. Straightforward method: DefInt A-Z Declare Function GetActiveWindow Lib "user" () Declare Function IsWindow Lib "user" (ByVal hWnd) Declare Function FindWindow Lib "user" (ByVal CName As Any, ByVal Caption As Any) Wnd = Shell("printman.exe", 1) 'run other app Wnd = GetActiveWindow() 'immediatly do this 'See if printman is still running: While IsWindow(Wnd) 'API call to determine is Wnd dummy = DoEvents() ' is valid; then use doevents() Wend ' to run other tasks 'To determine if an application is already running ask windows to find its 'title caption. Use this code... Function Loaded (Caption$) Loaded = FindWindow(0&, Caption$) 'use call If Loaded > False Then Loaded = True 'if caption found End Function example: If NOT Loaded("Program Manager") Then dummy = Shell("PROGMAN.EXE",1) 2. This example is more specific to someone's application: Dim PassString$ ' declare a local string to hold user's password Const SW_SHOWNORMAL = 1 Declare Function GetActiveWindow Lib "User" () As Integer Declare Function ShowWindow Lib "user" (ByVal hWnd As Integer, ByVal nCmdShow As Integer) As Integer Declare Function SetFocusAPI Lib "user" Alias "SetFocus" (ByVal hWnd As Integer) As Integer Form_Load() ' this executes when the PassWord form loads On Error GoTo NoLink ' setup our no-link error handler SystemLink.LinkMode = 1 ' try to establish a hot link with a server On Error GoTo 0 ' disable error trapping MsgBox "A DDE LINK HAS BEEN ESTABLISHED WITH A SERVER!!!" AppActivate "SalHist" ' activate the "other" SalHist hActive = GetActiveWindow() ' pickup it's hWnd handle Dummy = SetFocusAPI(hActive) ' give it the system focus Dummy = ShowWindow(hActive, SW_SHOWNORMAL) ' and restore its size End ' finally, terminate this app! On Error GoTo 0 ' disable our temporary error trapping SystemLink.LinkMode = 0 ' abandon our local DDE connection attempt LinkMode = 1 ' and establish ourselves as a DDE server... CenterForm PassWord ' (My standard.lib form-centering routine) PassString$ = "" PassWord.Show 1 ' present the PassWord form for user data ' entry. The "1" makes it "modal" (stickier) End Sub ' end of the Form_Load subroutine. --------------- Multimedia Time Format: The VB multimedia property "Position" returns a four-byte integer for HH:MM:SS based on the "TimeFormat" property. To access this four-byte integer, assuming you have the TimeFormat property set to 1 (MCI_FORMAT_HMS): HOURS= mmcontrol1.position AND &H000000FF& MINUTES=(mmcontrol1.position AND &H0000FF00&) / 2 ^ 8 SECONDS=(mmcontrol1.position AND &H00FF0000&) / 2 ^ 16 (Robert Eineigl) --------------- Name in Task List: The name that appears in the Windows Task List for a VB application is what you specify under Title in the Make EXE File menu. If you don't specify anything, it defaults to the MAK file name. --------------- Networks & VB: VBRUN100.DLL and VB apps (EXE's) should be placed on each machine's hard disk to avoid significant performance degradation. If you have VB on a network, you may find that it does not write its temporary files to the place you would like them. The solution is to set an environment variable to specify where the files should be written. Example: SET TEMP=C:\ --------------- Norton Desktop vs VB: Several Norton Desktop users have reported problems (freezeups) occurring after long sessions in Visual Basic. --------------- "Out of Stack Space" Error: This is usually caused by recursion (a loop where the same Sub/Function gets called over and over). The easiest way to detect this is to single-step through your program using the F8 key. Also see "Memory, Out of". --------------- Pausing Windows Execution: Q: How can I emulate the Pause button in VB? A: Any kind of loop in VB will tie up Windows if you do not have a DoEvents() in it. The problem is getting out of the loop, since you can't click on a button, etc. There may be an easier way to go about this, but off the top of my head, here is one way that works. (Note that any mouse clicks done during the loop will be acted on when the loop is done. The Hourglass icon may discourage such loops.) Global.Bas: DefInt A-Z Type PointAPI x As Integer y As Integer End Type Global CursPos As PointAPI Declare Sub GetCursorPos Lib "User" (lpPoint As PointAPI) Type Rect r_Left As Integer r_Top As Integer r_Right As Integer r_Bottom As Integer End Type Global PicRect As Rect Declare Sub GetWindowRect Lib "User" (ByVal hWnd, lpRect As Rect) Declare Function GetFocus Lib "User" () Form1_General_Declarations: DefInt A-Z Sub Command1_Click (): Mousepointer = 11 Picture1.SetFocus Picture1.Print "Move Mouse Here" GetWindowRect GetFocus(), PicRect Do GetCursorPos CursPos If CursPos.x >= PicRect.r_Left and CursPos.x <= PicRect.r_Right and CursPos.y >= PicRect.r_Top and CursPos.y <= PicRect.r_Bottom Then InPosition = -1 End If Loop While Not InPosition Picture1.Print "Free at last!" Mousepointer = 0 End Sub (Nelson Ford) --------------- Program Manager Groups, Getting List of: The following routine will get a list of program groups and their contents from Program Manager. This example uses a Text box to grab the data and transfers it to a List box array, but you could use other ways to store, manipulate, and display the data. Text1.LinkTopic = "ProgMan|Progman" Text1.LinkItem = "ProgMan" Text1.LinkMode = 2 Text1.LinkRequest On Error Resume Next Text1.LinkMode = 0 t$ = Text1.Text + Chr$(13) + " " x = InStr(t$, Chr$(13)) y = 0 Do While x > 0 List1(0).List(y) = Left$(t$, x - 1) t$ = Mid$(t$, x + 2) y = y + 1 x = InStr(t$, Chr$(13)) Loop For z = 0 To List1(0).ListCount - 1 Text1.LinkTopic = "ProgMan|Progman" Text1.LinkItem = List1(0).List(z) Text1.LinkMode = 2 Text1.LinkRequest On Error Resume Next Text1.LinkMode = 0 t$ = Text1.Text + Chr$(13) + " " x = InStr(t$, Chr$(13)) y = 0 Do While x > 0 List1(z + 1).List(y) = Left$(t$, x - 1) t$ = Mid$(t$, x + 2) y = y + 1 x = InStr(t$, Chr$(13)) Loop List1(z + 1).Refresh Next This works for Norton Desktop (from what I understand) also. (Link routines by Jeff Simms. List box routines by Nelson Ford) --------------- Resource Limits: You cannot just keep adding boxes and buttons at will. The limit on the number of controls on a form is 255, but you'll likely have resource limitation problems long before that. An example of resource conservation in Windows in general is to get programs out of Program Manager that you do not regularly use. To conserve resources in a VB app, try using a Grid box instead of a bunch of Text or List boxes to display a lot of information. You can also use Print to put captions at specific points on a Form instead of using Label boxes. --------------- Restart Windows: Declare Function ExitWindows% Lib "User" (ByVal dwReserved&, ByVal wReturnCode%) x% = ExitWindows(66, 66) (Paul Smith) --------------- Sendkeys - Immediate Processing: Change all your SendKeys statements so they use True as the second parameter. This will tell the other app to process the keys immediately rather than putting them into a buffer. SendKeys "Sue Landaiche", -1 SendKeys " ", -1 SendKeys "{TAB}", -1 SendKeys "(HELLO)", -1 i% = DoEvents() ' Let other program update its display (John Socha) --------------- SendMessage: In the Global module: 'type the declaration on one line Declare Function Sendmessage Lib "user" (ByVal Hwnd%, ByVal Msg%, ByVal wParam%, ByVal lparam As Any) As Long Global Const wm_WinIniChange = &H1A Then, for example: Sub Command1_Click x& = Sendmessage(&HFFFF, wm_WinIniChange, 0, ByVal "") End Sub --------------- Shell-to-DOS Problems: When you Shell to DOS to execute a command or a DOS application, Windows tends to want to iconize the Shell and continue with the rest of the program. In the following example, you would get an error in trying to open MYFILE because the COPY command has not been executed yet. x = Shell(Environ$("COMSPEC") + " /c COPY A:MYFILE C:", 3) Open "MYFILE" For Input as #1 In the next example, you would get a sharing violation and/or a string of DOS icons across the screen: For i = 1 to 10 x = Shell(Environ$("COMSPEC") + " /c dir c:\ >> c:\testshel", 3) Next The following code has been suggested as a fix. It does not work for me in the Standard Mode, but others have said it works for them. Declare Function GetModuleUsage Lib "Kernel" (ByVal hModule%) As Integer Sub Form_Load () For i% = 1 To 10 s% = Shell(Environ$("COMSPEC") + " /c dir c:\ >> c:\testshel", 1) While GetModuleUsage(s%) x% = DoEvents() Wend Next End Sub IMPORTANT NOTICE: Some machines will crash after shelling to DOS from Windows. It appears to happen only when a math chip is in the system and Windows is in the Standard mode -- and then not on ALL such systems. At first we thought this was a VB problem, but Keith Funk, Costas Kitsos, Ted Young and others have continued to dig into the problem and discovered that it can be duplicated (on SOME systems) with any program that uses the math chip. If you have a math chip, try the following: 1. Start Windows in Standard mode (ie: /S). 2. Run the Windows Calculator and minimize it to an icon. 3. Open and close a DOS window several times. After three or four times, if your system is one of the unfortunate ones, you should get an UAE. In Visual Basic, the problem manifests itself in this way: In Form_Load, put the following lines and run it: For i = 1 to 5 x = (Environ$("COMSPEC") + " /c dir c:\", 1) y = DoEvents() Next You might get a spurious "Illegal function call" on the DoEvents line rather than a UAE, but if you keep going, you will get the UAE. NOTE: Microsoft has acknowledged that this is a bug in Windows. It appears that all we can do is wait for Windows 3.1. Again -- this problem is limited to some machines with math chips that run Windows in Standard mode (normally, just 286's). --------------- Shell-to-DOS Syntax: To shell to DOS, you could use the syntax: x = ("c:\command.com /c ... but this might not give you the right location of command.com. Instead, use x = (Environ$("COMSPEC") + " /c ... as in the previous section. - Ted Young Jonathan Zuck has suggested the following method of shelling to DOS: In your declarations: DefInt A-Z Declare Function GetModuleUsage Lib "Kernel" (ByVal hInst) In your event/procedure code: Go$ = "c:\batfile.bat" hInst = Shell(Go$, 2) While DoEvents() And GetModuleUsage(hInst) > 0 Wend George Campbell points out that DoEvents() returns a value equal to the number of windows open and that if you have none opened or iconized, then the above routine will not work because DoEvents will return 0. --------------- Shell Error Numbers: The manual has an example that says: If Shell(File1.FileName, 3 - Index) < 32 Then ... The "32" is there because "Shell" always returns a value and any value equal to or greater than 32 would indicate an error in trying to Shell. --------------- System Modal Form: To turn a VB form into a System Modal window- Declare Function SetSysModalWindow Lib "User" (ByVal hWnd As Integer) As Integer oldmodal = SetSysModalWindow(yourformhWnd) (Robert Eineigl, Microsoft Product Support) --------------- Task List Exit Can't Be Stopped: Even with "Cancel=-1" in your Form_Unload sub, you cannot stop (in VB) someone from closing your app via the Windows Task Manager. --------------- Temp Files: If you are using large graphical objects such as bitmaps or many forms with many controls, then as you run and edit (even add a control or two), VB makes new temp files for each form to track the latest changes (for saving). If you use a Ram disk for Temp file space, a large project could easily exhaust the space. So either increase the size of the ram disk, or save more often, or go to a disk based temp directory. (Robert Eineigl, MS) --------------- VB App Cannot Be Windows 3.0 Shell: You cannot create a Program Manger replacement with a VB application in Windows 3.0, but you CAN in Windows 3.1. --------------- VBRUN100 Must Be on A:?: See "Control Box Exit Bug", above. --------------- Version (of Windows) Detection: DefInt A-Z Declare Function GetVersion Lib "Kernel" () As Long In your code... Ver& = GetVersion() '-- Make the API call WinVer& = Ver& Mod &H10000 '-- Extract the Windows version number DosVer$ = Ver& \ &H10000 '-- Extract the DOS version number '-- Extract the Major and Minor revisions and build the version strings: WinVersion$ = Format$(WinVer& Mod &H100) + "." + Format$(WinVer& \ &H100) DosVersion$ = Format$(DosVer& \ &H100) + "." + Format$(DosVer& Mod &H100) (Gregg Irwin) The GetVersion API call will report "3.00" for both versions 3.00 and 3.00a since the minor version is still 00 for both. There is no way to detect the "a". Windows 3.10 should report in hex "A03". --------------- Wallpaper Changing: Routine 1 uses an undocumented feature of Windows 3.0 to change wallpaper. This does not change WIN.INI nor the Windows Palette. Routine 2 uses a feature of Windows 3.1 to change the wallpaper, as well as changing WIN.INI. This feature also changes the Windows Palette. This method does not work with Windows 3.0, so you may need to use both versions. Try method 1 first and "on error", goto to method 2. 1. For Windows 3.0 Only: ======================= Declare Sub SetDeskWallPaper Lib "User" (byval FileName$) Call SetDeskWallPaper(f$) 'where f$ is the name of a BMP file will change the wallpaper in Windows, but it will not repaint the screen (ie: make the new wallpaper visible) automatically. To accomplish that, you must use the following: Declare Sub ForceRedraw Lib "User" Alias "InvalidateRect" (ByVal hWnd As Integer, \ lpRect As Long, >-all this on the line above in your app. ByVal bErase As Integer) / Call ForceRedraw(0, 0&, 0) 2. For Windows 3.1 only: ======================= Declare Sub SystemParametersInfo Lib "User" (ByVal wAction%, ByVal wParam%, lParam As Any, ByVal fWinIni%) Const SPI_SETDESKWALLPAPER = 20 Const SPIF_UPDATEINIFILE = 1 'update Win.ini Const SPIF_SENDWININICHANGE = 2 'update Win.ini and tell everyone SystemParametersInfo SPI_SETDESKWALLPAPER, 0, ByVal BMPFile$,_ SPIF_UPDATEINIFILE (Jonathan Zuck) --------------- Why VB Apps Don't Cascade/Tile: Forms in VB apps are actually pop-up forms. The VB parent form is invisible. This is why functions such as Cascade/Tile in Task Manager will not affect VB apps. --------------- WIN.INI - Force Windows Reread: If you make changes in WIN.INI, other Windows apps don't know about it unless you broadcast a message about telling them to reread WIN.INI: Global Const WM_WININICHANGE = &H1A Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Any) As Integer x% = SendMessage(&HFFF,WM_WININICHANGE, 0, ByVal "") My understanding is that you don't put anything in the last argument; i.,e., WM_WININICHANGE isn't meant to be specific, just "something changed", and the other app is responsible for figuring out what changed. My small app that uses this is a printer utility. My experience is that not all of the ProgMan applets reflect the change. For example, I found that when I changed a printer in WIN.INI, Write didn't recognize the change, while Paintbrush did. All other apps I tested, such as Ami Pro, Word, etc. did reflect the changes in their printing dialogues. (Doug Beebe) --------------- WIN.INI Warning: If you use WriteProfileString while WIN.INI is also an open file in an app, the data being written will end up in a lost chain. - Dennis Harrington You can get rid of the lost chain by doing a CHKDSK /F, but don't forget that if you do a CHKDSK /F in a Windows 3 DOS window, you will corrupt your hard disk, so exit Windows first. --------------- "&" in Captions: When you put "&" in a Caption, it is a sign for VB to use the next letter as a shortcut key. If you really want to display "&" as part of the caption, use a pair of ampersands: For example, Label1.Caption = "&&HFA3" will display the caption "&HFA3". --------------- ActiveControl Property: If you have a generic subroutine that you want to act on just a specific type of control, you can use the Screen's ActiveControl Property. Example: Sub SelectAll () If TypeOf Screen.ActiveControl Is TextBox Then Screen.ActiveControl.SelStart = 0 If Len(Screen.ActiveControl.Text) = 0 Then Screen.ActiveControl.Text = " " 'Improve cursor visibility End If Screen.ActiveControl.SelLength = Len(Screen.ActiveControl.Text) End If End Sub (Chuck Welborn) --------------- Aligning Text & Format$ Padding: 1. Only one VB control offers the option of text (Left, Center, Right): a Label. Third-party controls often add this option to other controls. 2. You can manually align text using spaces and Format$, but with great limitations. First problem: most fonts are not mono-spaced and thus will not line up in columns nicely. Second problem: Format$ does NOT pad with spaces the way PRINT USING did. For example: List1.Additem "X" + Format$(1.2, "#####.##") results in: "X1.2" To align numbers, use a mono-spaced font and - Right$( Space$(nn) + Format$(___, "__________"), nn) Example: Right$( Space$(10) + Format$(1.2, "#######.00"), 10) results in " 1.20" 3. A method of aligning proportional text is with TextWidth: NumFmt$ = "##,###" Numb$ = Format$ (Amount, NumFmt$) CurrentX = CurrentX + TextWidth (NumFmt$) - TextWidth (Numb$) Print Numb$ --------------- Aligning Text on a Form: "SetTextAlign" is a superior way to center text. It not only affects the output alignment of the API "Textout" call, but the standard VB "Print" statement reacts to it as well. Use the following to print a centered line of text on your form: Global: Global Const TA_NOUPDATECP = 0 Global Const TA_UPDATECP = 1 Global Const TA_LEFT = 0 Global Const TA_RIGHT = 2 Global Const TA_CENTER = 6 Global Const TA_TOP = 0 Global Const TA_BOTTOM = 8 Global Const TA_BASELINE = 24 Declare Function SetTextAlign Lib "GDI" (ByVal hDC As Integer, ByVal wFlags As Integer) As Integer Form_Click: Lbl$ = "Centered title" CurrentX = TheForm.ScaleWidth/2 CurrentY = 100 'or whatever... Result% = SetTextAlign(TheForm.hDC, TA_CENTER + TA_BOTTOM) Theform.Print Lbl$ 'centers output; use other constants for other alignments (Bob Scherer) --------------- Button Colors: The BackColor property has (virtually) no effect on the color of the button. Button colors are controlled by WIN.INI. See the 6/11/91 issue of PC Mag for details. Of course, changes you make there apply globally. An alternative is to simulate a button with a Picture box and a bitmap. --------------- Buttons with Pictures: Making a picture control behave like a pushbutton is relatively easy (J. D. Evans, Jr.): 1. Draw a bitmap. 2. Use the bitmap in a picture control with its DrawMode set to Invert. 3. On GetFocus and LostFocus, draw a 1 or 2 width line around the picture control to highlight it (use 0,0 and ScaleWidth-ScaleHeight, black, and B options) the way a button normally looks when it gets focus. 4. Have the click event call KeyDown and pass it a KEY_ENTER and then make KeyDown do what you would normally do in a pushbutton click for both KEY ENTER and KEY SPACE (to emulate keyboard). 5. On MouseDown and MouseUp do the same thing as you did in GotFocus and LostFocus, except specify NO COLOR parameter and use BF option. the INVERT DrawMode (or style or whatever it is) will take care of making the picture look like a button going up and down. Note: if you do not actually do anything in the KeyDown (e.g., call a function or display something), then the picture may get confused as to whether it should be up or down, but then why have a button that actually does nothing other than go up and down! It needs to do something to give everything a chance to get thru the message queue in correct sequence. This technique is courtesy of Karl Peterson (who told it to me) and results in a very efficient graphic pushbutton that paints QUICKLY! P.S. Use Windows Paint to make the BMP file. Alt-PrintScrn will capture the current window to the ClipBoard and you can paste it into Paint and make a BMP that looks just like a pushbutton or anything else! P.P.S. When I say 'draw a line' (in 2.) I mean use the DrawLine method/function. It will make sense when you look it up. It looks complicated but is about 15 lines of code. Actually drawing the bitmap in Paint is the thing that takes the most time, but you can make some really neat looking buttons, and all with vanilla VB controls. --------------- Buttons with Two Lines?: Q: Is there a way to put two-line captions on buttons? A: No, but you can use a Picture box (see above) to look like a button and use as many lines as you want. Third-party add-on controls support two lines. --------------- Loading Custom Controls: To save time loading all the custom controls each time you start a project, have a "master" app with all the VBX's in it. Load it and save it immediately to a new project name. --------------- Modal Bugs: 1. If you call the Printform method from within a modal window, the window then loses its modality. (Jack Freudenheim) MS has acknowledged this as a bug and offers this work-around (Don Funk): 'form that is displayed modally Sub Command1_Click() Const GWL_STYLE = -16 Const WS_DISABLED = &H8000000 MousePointer = 11 PrintForm OldStyle& = GetWindowLong(Form1.Hwnd, GWL_STYLE) X& = SetWindowLong(Form1.Hwnd, GWL_STYLE, OldStyle& Or WS_DISABLED) MousePointer = 0 End Sub 2. A call to InputBox$ will also destroy a form's modality. (Bob Kubelka) --------------- Modal Form Switching Bug: A couple of people have reported that if you have one modal form call up another modal form (which then exits back to the original modal form) and this call-return procedure is repeated several times, you will get an "out stack space" error message. The fix is to set focus to a 3rd form's control from either of the two modal forms so that the two modal forms to not switch focus back and forth. Set a global variable called gDirection. In the event which calls the 2nd modal form (like ModalFrm1.cmdModalFrm2_Click), set this variable to "2" and do a "MainForm.cmdOK.Setfocus" AFTER your "Unload ModalFrm1" command. In the Gotfocus event of the Mainform.cmdOK, test for the value of gDirection and respond with either a "ModalFrm2.Show 1" or a "ModalFrm1.Show 1". You can either unload the 1st modal form when switching between the two, or you can hide it for faster showing the second time around. If you opt for the hide method, be sure to explicitly Unload both modal forms in the Mainform.Unload procedure. If you do not have that 3rd visible underlying form, then you're out of luck. (Bob Scherer) --------------- MsgBox Limitations: You cannot position a MsgBox at will, nor change its colors, etc. As an alternative, consider creating your own MsgBox with a small form. --------------- No DblClick in Directory List Box: The documentation for the DblClick event suggests that the directory list box gets this event. This is a documentation error. --------------- Scroll Bars - Automatic and Manual: Vertical scroll bars appear automatically in List Boxes and Combo Boxes when the amount of data exceeds the space available. For an illustration about how to use the Vertical Scroll Bar control to page through data in a file or an array and display a little at a time in a box, see RANDGR.EXE and HUGEGR.EXE on DL1 or DL6 of the forum. --------------- Toolbar Rearranging: You can't change the shape/size of the toolbar, but you can force VB to do so! Move the toolbar down on the screen so that the last row or two of the controls is below the visible portion. Then exit and restart VB. VB will reconfigure the toolar for you! (Mark Novisoff) --------------- Changing Text: Text is read-only, but you can change it by using "Combo1.ListIndex = 3". This would select and display the 4th item in the list. --------------- Color: BackColor does not apply to the list portion of the combo box, only the 'edit' part. --------------- Detecting Change: In a Combo Box, you only get a CHANGE event if the user actually types inside the edit control portion of the control, not if they select an item from the list. To detect that the user has selected an item from the list, you must use either the Click or DblClick events. Using the arrow keys has the same effect as .Click. Reportedly there is no keyboard equivalent of DblClick, although you can simulate it by setting the default to your "OK" button. To determine if a Click actually changed what was in the edit box, you will have to set up a variable with the initial data and compare it after the Click. --------------- Pseudo Combo Dropdown List Box: To implement a pseudo Combo DropDown List Box like the one used in the VB Help/Index/Search Window: As you type text in a Text Box, VB hilights the first entry in the List Box that matches what you have typed. This can be implemented in VB by using a Text Box, a List Box and the API message LB_FINDSTRING. --------------- Slow Loading of Combo Boxes: Combo Boxes are much slower to load data into than List Boxes. Some people prefer to make their own "Combo Boxes" by combining a Text Box and a List Box. The API CB_ADDSTRING loads a Combo Box 5-10 times FASTER than using the VB AddItem method. - Bill Reilly --------------- "Bad DLL Calling Convention": If you get the above error message with a Declare like the following: Declare Function bitblt Lib "Gdi" (ByVal destHdc, ByVal X, ByVal Y, ByVal w, ByVal h, ByVal srcHdc, ByVal srcX, ByVal srcY, ByVal Rop As Long) it is because you need "%" of "as integer" after some of the arguments. Alternatively, you can use DefInt A-Z, but when using Declares, be sure to pay attention to the type of each variable. --------------- Ending an App from a DLL: If you have a system menu with the CLOSE option, then you could make the following call from within your dll: X& = SendMessage(Handle, WM_SYSCOMMAND, SC_CLOSE, NULL) where Const NULL = 0& Const WM_SYSCOMMAND = &H112 Const SC_CLOSE = &HF060 Don Funk --------------- MDI Activate Endless Loop: If you call a MsgBox in the PTK's MDI's Activate event, then when you click Ok on the MsgBox to exit it, the MDI's Activate event will be fired again, calling the MsgBox again, and creating an endless loop. The same thing happens if you .Show Modal a form from the Activate event. (Raymond Six) --------------- Meter From Picture Boxes: To make a Meter control from Picture boxes, follow these steps: 1. Draw a Picture box and adjust it to the size you want. 2. With the Picture box selected, press Ctrl+Ins to copy it to Clipboard. 3. With the Picture box still selected, press Shift+Ins to create a second box within the first one. 4. When asked if this is an array, say No, and it will be named Picture2. 5. Set Picture2's BorderStyle to 0 in the attributes menu. 6. Set Picture2's BackColor to a contrasting color. 7. Declare StepValue and MaxValue as global variables in Global.bas. 8. Enter the following code in your app: Sub Form_Load () Picture2.Width = 1 MaxValue = (some number) StepValue = Picture1.Width / MaxValue End Sub To increase/decrease the gauge: Picture2.Width = Picture2.Width +/- StepValue (By Jussi Mattila) --------------- METER1.VBX Bug: METER1.VBX is a graphic meter control on DL1 or DL6. It allows specification of max items and number of items complete, and it computes percent complete and displays graphical Percent Complete bar. If the number of max items are greater than 100, than you get inconsistent and crazy results (sometimes). I worked around by always using 100 as max items, and did the scaling myself. --------------- METER1.VBX Display Update: To get the meter display to reflect changes, add the following in your loop: Meter1.Refresh --------------- Sound.DLL: Here's my DLL written in TPW. I had no documentation besides the Windows Programmer's Reference so maybe someone here can tell me if I'm on the right track. Some questions come to mind such as: how large should I make the voice queue? Is it necessary to open and close the sound device every time I want to set a note? library SoundDLL; uses WinTypes, WinProcs; procedure PlayNote(Note, nLength, Cdots: Integer);export; begin OpenSound; SetVoiceAccent(1,100,255,S_NORMAL,0); SetVoiceQueueSize(1,1000); SetVoiceNote(1,Note,nLength,Cdots); StartSound; WaitSoundState(S_QueueEmpty); CloseSound; StopSound; end; exports PlayNote index 1; begin end. The declaration in VB (general): Declare Sub PlayNote Lib "e:\tpw\output\soundll.dll" (ByVal note%, ByVal Length%, ByVal Cdots%) (Mark N.): The size of the voice queue is one of those numbers that you simply "Pull out of thin air". It depends on what you're going to do. For example, in VBTools, we set the queue to 8K because we include several large pieces of music. OTOH, if you're going to play single notes on an occasional basis, then 1K should be plenty. It is not necessary to open and close the sound device every time. In fact, if you close it while there are notes in the queue, they'll be lost! I suggest that you do what we've done in VBTools: 1. Open the sound device when the user first calls your proc. 2. If the device is open, then close it when your DLL unloads. 3. Give the user a method to close it so that sound can be generated by other apps. --------------- DLL's Explained: A DLL is a dynamic link library which can be loaded and used by numerous programs at the same time. This saves memory since if it is done the "old" way by linking in the libraries at compile/link time, every program that uses the code would have to have the same code inside the "EXE". An example is the PRINT statement in QBX. Every program that you have written (except the BRUN code) has the same routine that performs the Print routine. In Windows, you can remove this code from your EXE and put it into a DLL. That way when you have several VB programs that use the Print routine, there is only one copy of this routine in memeory. There are several DLL's used by Windows. USER, GENERAL, KERNEL, and GDI are the 4 main DLL's used by Windows. They have in "their" code functions used by all programs in the Windows environment. Things such as positioning the window, painting the background, and moving the cursor are a few examples of functions used by all the programs in the environment. There is an excellent book called MS Windows Programmer's Reference. This manual lists all(well 99% of them) functions that can be called from VB. Not all functions can be called from VB, and not all of them would you want to call, but it does list all of them, so you can take your pick. It's a pretty hefty manual but quite easy to read. You can get it from MS by calling 1-800-426-9400 and asking for kit 1-55615-413-5, called "MS Windows Programmer's Reference Manual and Online Resource". It's is 49.95 but well worth it. It's about 800-1000 pages. --------------- DLL's Are Slow: If you call a C routine in a DLL that assigns strings it will be slower than a direct assignment in Visual Basic. Even if the assignment routine is just as efficient as VB's, you are adding the overhead of extra calls to get the same job done. If you wrote the DLL code in pure .asm instead of calling another routine it would probably be closer to VB's time. --------------- ByVal When Passing Strings: I have a question regarding the use of 'C' style DLL return structures. So far I've figured out that strings can be used to return values by declaring them ByVal if the item returned is an array (i.e. char bozo[20]). What I can't figure out is how to get a string which is returned as char far *. What does ByVal do when passing strings? My understanding is that this means that the contents of the var. should not be modifiable, but this does not seem to be the case when passed to a DLL. When passing non-strings to DLLs, does omitting the ByVal mean that it can be updated by the call?? For example, should parameter declared short far * be passed with or without ByVal? Fm: Jonathan Zuck (UFI) 76702,1605 Passing a string ByVal tells VB to append a NULL to the end of it and pass an LPSTR to the string. It *is* therefore able to be modified by the DLL as long as the DLL is capable of looking for the terminating character. These changes will get reflected in the VB string. This leads me to you next question. The way to deal with functions that return a pointer to a string is to use the LSTRCPY API function to which you would pass your VB string (padded with enough spaces) ByVal and the LPSTR you got back. There is a function in QuickPak/VB that will simply convert an LPSTR to a VB string if you have that package. Yes, if you want the DLL to be able to change a numeric parameter, you should *not* pass it ByVal. Also make certain that the DLL is expecting a pointer and not the value. However, if it plans to change the variable, it would be expecting this anyway. --------------- TPW Framework for DLL's: From Rick Sands: Here is the basic framework for a Turbo Pascal DLL: LIBRARY Pas; Uses WinTypes, WinProcs; Function FileExist(Filename:pChar):Integer; EXPORT; { <<-- NOTE } var Fp:File; begin assign(Fp, Filename); {$I-} reset(Fp,1); {$I+} { Open file as Binary } if IOResult = 0 then { Did it open? } begin FileExist := -1; { VB True } Close(Fp) { Don't forget to close } end else FileExist := 0 { VB False } end; EXPORT FileExist Index 1; { <<-- NOTE } begin end. In VB, Declare Function FileExist Lib "PAS.DLL" (byval Filename as String) as Integer --------------- Passing Records: Type MyType A As Integer B As Long End Type Dim Foo(10) As MyType X = SomeDLLFunction(Foo(1)) The DLL will receive a pointer to element 1 of the array. Because user-defined types are stored one after the other, the DLL needs to know the length of each record, which lets it calculate the address of the subsequent records. (Mark Novisoff) --------------- Passing Strings: From John Kesler: Thanks to Jonathan Zuck and Mark Novisoff for providing the suggestions used here to demonstrate a call to the Paradox Engine function pxTblCreate%. This workaround addresses the problem that VB cannot directly pass an array of strings to a DLL written for C. As you can see below, the technique is to append null values to elements in string arrays (so they "look" like zStrings) and then build an arrays of Long Ints that point to the address of the strings. As I understand it, the DLL gets a pointer to the first string element and finds the pointers to the other elements one after the other. Global: Declare Function pxTblCreate% Lib "pxengwin.dll" (ByVal tblname As String, ByVal nflds As Integer, fnames As Long, ftypes As Long) 'In your form or module. 'create arrays to hold field names and types Static vbnames(2) As String Static vbtypes(2) As String 'append null value to each string element vbnames(1) = "First" + Chr$(0) vbnames(2) = "Second" + Chr$(0) vbtypes(1) = "A1" + Chr$(0) vbtypes(2) = "A2" + Chr$(0) 'create arrays to hold the segment selector & offset for each element Static names(2) As Long Static types(2) As Long 'MicroHelp function SSEGADD& returns the segment selector & offset of 'each string element For x% = 1 To 2 fnames(x%) = ssegadd&(vbnames(x%)) ftypes(x%) = ssegadd&(vbtypes(x%)) Next x% 'call the pxTblCreate% function with the first element of each array retval% = pxtblcreate%("Test", 2, names(1), types(1)) From Jonathan Zuck: Great job! Remember that these addresses are subject to change given any further string assignments. Make certain to recalc their SSEGADDs each time before you use them. Also, the Windows API includes a function that can be used as a SSEGADD function if you don't have one of the commercial DLLs. The drawback is that it capitalizes the strings but that ain't a problem in this context. Declare Function SSegAdd& Lib "User" Alias "AnsiUpper" (byval StrVar$) From Jeremy McCreary: AnsiPrev offers an even better work-around for VB's lack of pointer support because it doesn't alter the data passed to it. The VB code fragments below show the essentials: Type LongIntType ' Structures needed for type Value as Long ' coercion in MKL$() below End Type Type FourByteStrType Bytes as String * 4 End Type Declare AsniPrev Lib "User" (ByVal vbStr1$, ByVal vbStr2$) As Long Pointee& = RGB(192,220,192) ' Numeric data needing a pointer StringVersion$ = MKL$(Pointee&) ' Make a string representation LongPointer& = SSegAdd(StringVersion$) ' Voila! A far pointer to the data Function MKL$ (num As Long) ' Adapted from Hank Marquis' nifty Dim LInt as LongIntType, LIntStr as FourByteStrType ' NUMBER.ZIP in DL5 LInt.Value = num LSet LIntStr = LInt ' Copy number into fixed string, MKL$ = LIntStr.Bytes ' then into VB string End Function Function SSegAdd (vbStr$) As Long ' The trick is to pass AnsiPrev SSegAdd = AnsiPrev(ByVal vbStr$, ByVal vbStr$) ' the same string twice! End Function The example here is trivial, but the technique can be modified to get long pointers to just about any kind of data type, even arrays. Pointer support is essential when calling DLL procedures (e.g., common dialog functions) that take as parameters data structures containing pointers. --------------- SLIDER Control Bug: SLIDER is a free custom control. In the SLIDER custom control example, there is an error which causes the control to either repeat an erroneous value or show no slider knob. The MODEL flag MODEl_fLoadMsg has been omitted which suppresses the VBM_CREATED message the control is waiting for to reset it's knob=value to zero. Also the MODEL_fArrows flag NOT MODEL_fGetArrows flag must be set. (Manfred Waldmeyer) --------------- VBPro MDI Child Creation at Runtime: The MDI children control arrays can be created at run time. Put the seed on the form and set the index to 0 and the visible property to false, the controlname to z and maximize a form. For i% = 0 To 7000 Step 3000 For j% = 0 To 3800 Step 1900 If i% <> 0 Or j% <> 0 Then Load z(k%) z(k%).caption = Str$(k%) z(k%).left = i% z(k%).top = j% z(k%).visible = -1 z(k%).active = -1 k% = k% + 1 End If Next j% Next i% (Robert Eineigl) --------------- Centering a Form on Screen: To center a form on screen: Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2 (Ted Young) --------------- Controlling Form Size: Set MaxButton to False so the user can't maximize the form. General_Declarations: Dim OldWidth As Single '-- width before resizing. Dim OldHeight As Single '-- height before resizing. Const MinWidth = 6000!, MaxWidth = 9000! '-- change these values to... Const MinHeight = 3000!, MaxHeight = 6000! '-- the ones you want. Sub Form_Load () OldWidth = Width OldHeight = Height End Sub Sub Form_Resize () If WindowState <> 1 then '-- allows user to minimize window. If Width < MinWidth Or Width > MaxWidth Then Width = OldWidth Else OldWidth = Width End If If Height < MinHeight Or Height > MaxHeight Then Height = OldHeight Else OldHeight = Height End If End If End Sub --------------- Control's Location in Pixels: The following routine will give you a control's screen coordinates: Type RECT left As Integer top As Integer right As Integer bottom As Integerntrol) End TypeSetFocus Declare Function Getfocus Lib "User" () As Integer Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect As RECT) Global myrect As RECT CtrlName.SetFocus GetWindowRect Getfocus(), myrect Print myrect.top, myrect.left, myrect.bottom, myrect.right (Robert Eineigl, Microsoft Product Support) --------------- Copying/Moving Controls: You can copy or move one or more controls from one Form, Frame or Picture box to another using the Clipboard: 1. Click on the source Form, Frame or Picture box. 2. Hold the Ctrl key and, one by one, click on all the controls you want to copy. 3. Press Shift-Del (to move) or Ctrl-Insert (to copy). 4. Click on the destination Form, Frame, or Picture box. 5. Press Shift-Insert. --------------- Covered Form?: Q: I would like to know if there is a way to tell if a window is completely or partially covered by another window in Visual Basic. Even if the covering window is not in the same application. A: No - nothing straightforward and/or foolproof, anyway. --------------- Cursor in First Text Box: Q: When my program starts, how do I get it to put the cursor into the first text box so that the user can start entering data in it immediately? A: In the design mode, set the boxes TabIndex (in the Properties list) to 0. During runtime, you can say something like: Text1.SetFocus --------------- "Floating" Window (Forcing Window to Top): (Also see "Overlapping Controls", below.) Sometimes you may want a small window to show above the current window, but VB does not offer this feature. Routine 1 (courtesy of Ted Young) only works with Windows 3.0. Routine 2 (from Mike Mezaros) only works with Windows 3.1. 1. For Windows 3.0 only: ======================= Global.Bas: Declare Sub SetWindowPos Lib "User" (ByVal hWnd As Integer, ByVal hWndInsertAfter as Integer, ByVal X as Integer, (put this all on --> ByVal Y as Integer, one line) ByVal cx as Integer, ByVal cy as Integer, ByVal wFlags as Integer) Declare Function GetWindow Lib "User" (ByVal hWnd as Integer, ByVal wCmd as Integer) As Integer ' Set WindowPos Flags: Global Const SWP_Nosize = &H1 Global Const SWP_NoMove = &H2 Global Const SWP_NoActivate = &H10 Global Const SWP_ShowWindow = &H40 Form1, Load: Form2.Show ' this is the "Floating" window End Sub Form1, Timer1 (set Interval to 50 in Properties) Sub Timer1_Timer If GetWindow(Form2.hwnd,0) <> Form2.hWnd Then wFlags = SWP_Nomove or Swp_Nosize or Swp_ShowWindow or Swp_NoActivate SetWindowPos Form2.hWnd, 0, 0, 0, 0, 0, wFlags End If End Sub 2. "Stay on Top" for Win31 only: =============================== Declare Function SetWindowPos Lib "User" (ByVal hWnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer) As Integer wFlags = &H2 Or &H1 Or &H40 Or &H10 On_Top% = SetWindowPos(Form1.hWnd, -1, 0, 0, 0, 0, wFlags) Normal% = SetWindowPos(Form1.hWnd, -2, 0, 0, 0, 0, wFlags) (Mike Mezaros) --------------- Form Size and Granularity: Check the "Granularity" under the Desktop settings in the Control Panel. If this number is anything but zero, you'll get the effect of all windows only being able to be sized and placed by increments of 16 pixels multiplied by the Granularity number. Set it to zero and this should fix things. --------------- Form Wipes, Fades, Etc.: You cannot "fade out" a form with screen colors, since VB is limited to 16 colors, even in SuperVGA, but you can do wipes and fades. Example: s = 25 'number of steps to use in the wipe hx = Form1.Height / s 'size of horizontal steps wx = Form1.Width / s 'size of vertical steps For i = 1 To s - 1 Move Left + wx \ 2, Top + hx \ 2, Width - wx, Height - hx Next Unload Form1 (Also see Screen Wipes under "Screen".) --------------- Grouped Controls: If you want to group a set of controls within another control, such as a Frame, you cannot do so by double-clicking the control and moving it into the Frame. You must single-click the control and then click-and-drag INSIDE the frame (or other control) to draw the added control. --------------- Iconizing All Forms in a Project at Once: You can make all the visible windows in your program minimize to one icon when a "main" window is minimized, and then all be restored when that icon is double clicked. Put some code in the main form's Resize event that tests the WindowState to see if it's been minimized (WindowState=1), then change the Visible property of all your other forms (this would make only the Main form an icon, if you want to iconize the other forms instead of hiding them, you'd change the WindowState of the other forms). There's an example of this in the VB Lang Ref pg 331 (WindowState). When you minimize all the forms, an icon will appear for each one. If you only want one icon to appear, use the syntax "FormName.Visible = 0" to hide the others. --------------- Instant Display of Controls: To make controls appear more quickly, try placing all the controls in a Picture box with no border and the same background as the form. Leave the picture box invisible until all the controls are drawn, then make it visible. (Dennis L. Harrington) --------------- Minimizing All Forms At Once: If you have one main form and several subsidiary ones, you may want to be able to minimize all the forms at once when the main form is minimized. The easiest way is to set up a Global flag for keeping track of which forms are loaded: Global: Global Form2Loaded Global Form3Loaded, etc. 'in each form: Sub Form2_Load: Form2Loaded = -1 Sub Form2_UnLoad Form2Loaded = 0 Sub MainForm_Resize: If WindowState = 1 then 'if main form minimized If Form2Loaded then Form2.Visible = 0 If Form3Loaded then Form3.Visible = 0 etc Else 'main form maximized If Form2Loaded then Form2.Visible = -1 etc end if --------------- Move Disabling: You can keep a user from moving a form by setting BorderStyle in the Form's Properties list to "0 - None". --------------- Moving an Icon: The following code successfully moves both icon and caption of two forms to the upper left corner of the desktop. Declare Sub MoveWindow Lib "User" (ByVal hWnd As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal bRepaint As Integer) Declare Function GetWindow Lib "User" (ByVal hWnd As Integer, ByVal wCmd As Integer) As Integer Const GW_HWNDNEXT = 2 ' in the form_Click event of form1 form3.windowstate = 1 form3.Show MoveWindow (GetWindow(form3.hWnd, GW_HWNDPREV)), 0, 32, 32, 16, -1 form3.Move 0, 0 form3.Refresh form2.windowstate = 1 form2.Show MoveWindow (GetWindow(form2.hWnd, GW_HWNDPREV)), 62, 32, 32, 16, -1 form2.Move 62 * 15, 0 form2.Refresh (Robert Eineigl; Microsoft Product Support) You can use SetWindowPlacement() to change the RECT of your forms while they are minimized: Type RECT Left As Integer Top As Integer Right As Integer Bottom As Integer End Type Type POINTAPI x As Integer y As Integer End Type Type WINDOWPLACEMENT length As Integer flags As Integer showCmd As Integer ptMinPosition As POINTAPI ptMaxPosition As POINTAPI rcNormalPosition As RECT End Type Declare Function SetWindowPlacement Lib "User" (ByVal hwnd As Integer, lpwndpl As WINDOWPLACEMENT) As Integer Global PlaceForm As WINDOWPLACEMENT Global Const SW_SHOWNA = 8 Dim FormRect As RECT Dim FormMax As POINTAPI Dim FormMin As POINTAPI Sub Command1_Click () WindowState = 1 FormRect.Top = 0 ' this is just a test rect FormRect.Bottom = 200 FormRect.Right = 200 FormRect.Left = 0 PlaceForm.length = 22 PlaceForm.flags = 0 PlaceForm.showCmd = SW_SHOWNA PlaceForm.ptMinPosition = FormMin PlaceForm.ptMaxPosition = FormMax PlaceForm.rcNormalPosition = FormRect i% = SetWindowPlacement(hwnd, PlaceForm) End Sub (Jeff Simms) --------------- Moving Controls Cleanly: When resizing a form and moving the controls around on it, the screen can look very messy. Hiding the form causes a lot of flickering. Instead of hiding the form, render invisible the controls being moved until they are all moved then make them visible. More code but more esthetic too! (Robert Eineigl, Microsoft Product Support) --------------- Moving Form Without Title Bar: A user normally moves a form by clicking and dragging on the title bar. If you have a form without a title bar, you have to make other arrangements if you want to allow the user to move the form. Set a flag in the Form_MouseDown event (eg AllowFormMove=TRUE) and store the mouse position (eg MStartX=X MStartY=Y). Then in the mouse move event: If AllowFormToMove=TRUE then Form.Left=Form.Left+(X-MStartX) Form.Top=Form.Top+(Y-MStartY) End If In the Form_MouseUp event reset the flag ( AllowFormToMove=FALSE ). (Ian Taylor) --------------- Moving Multiple Forms Together: If you have a related set of forms and would like to move them all if the user drag-moves any one of them, it is easy enough to do by setting .Top and .Left for each form back to their original relative values when you detect a form has been moved. The problem is detecting when a form has been moved. Here is a method (suggested by Jonathan Zuck): Start your program from a Sub_Main and do the checking in there: Sub_Main: Load Form1 (and Form2 and Form3, etc) Do x = DoEvents() If Form1.Top <> Form2.Top (etc.) then MoveForms Loop End Sub Sub MoveForms: (Either use flags to determine which of the three forms has moved, or check to see which 2 forms are in the same place, which means the other form is the one that moved, then move the other forms.) End Sub The effect is that "Load Form1" starts your program running, but any time the program is awaiting user action, program flow will be in the Do-Loop in which the control locations are constantly being checked. The DoEvents in the loop allows other things to be done in Windows and in your app. --------------- Multiple Instances of a Form Not Supported: Many people have expressed the desire to set up a form and use multiple instances of it during run-time to display different data sets. This is known as "MDI" and VB does not support it directly, although the MS Professional Toolkit and other 3rd-party add-on's support it. One alternative to buying add-on's is to make as many duplicate forms as you think you might need. Another alternative is to make a separate EXE file with the form that you want to use multiple times. Then display it by shelling to it, which you can do multiple times. You can pass data to it via DDE or a text file. --------------- Norton's Desktop, Get in Front of: If you are using Norton Desktop for Windows and you have a startup form that loads your main form, the main form may be displayed *behind* the Windows/Norton groups of icons when you SHOW it. In your SUB MainForm_Load, put the SHOW statement *before* you UNLOAD StartupForm, rather than after. (Bob Craig) --------------- Control's Coords. Are Relative to Form: Note that when you specify coordinates for the placement of controls within a Form, the coordinates start with 0,0 at the top, left corner of the Form; they are NOT screen coordinates. (Bob Craig) --------------- Overlapping Controls: (Also see "Floating" Window, above.) VB does not directly support overlapping controls. (Controls placed inside Frames and Picture boxes are not considered overlapping controls.) Overlapping controls were considered not possible in VB at all until Keith Funk came up with the following: (for a complete demo program, get CLPSIB) 1. Put the following in the form's General Declarations. You can also put it in Global.Bas if you change the Declare's, Const's and Dim's to Global's. Declare Function GetFocus Lib "User" () Declare Function ControlhWnd Lib "ctlhwnd.dll" (Ctl As Control) As Integer Declare Sub SetWindowPos Lib "User" (ByVal hWnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer) Declare Function GetWindow Lib "User" (ByVal hWnd As Integer, ByVal wCmd As Integer) As Integer Declare Function GetNextWindow Lib "User" (ByVal hWnd As Integer, ByVal wFlag As Integer) As Integer Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal nIndex As Integer) As Long Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Long) As Long Const WS_CLIPSIBLINGS = &H4000000 Const SWP_NOSIZE = &H1 Const SWP_NOMOVE = &H2 Const GW_HWNDNEXT = 2 Const GW_CHILD = 5 Const GWL_STYLE = (-16) Dim hWndChild As Integer Dim Style As Long 2. Create the following subroutine: Sub SetClipSiblingBit (ByVal hWndParent As Integer) hWndChild = GetWindow(hWndParent, GW_CHILD) Do Style = GetWindowLong(hWndChild, GWL_STYLE) Style = Style Or WS_CLIPSIBLINGS Style = SetWindowLong(hWndChild, GWL_STYLE, Style) If GetWindow(hWndChild, GW_CHILD) <> 0 Then Call SetClipSiblingBit(hWndChild) End If hWndChild = GetNextWindow(hWndChild, GW_HWNDNEXT) Loop Until hWndChild = 0 End Sub 3. Put the following in Form_Load Form1.Show Call SetClipSiblingBit(Form1.hWnd) 'get hWnd for the controls: Grid1.SetFocus G = GetFocus() Text1.SetFocus t = GetFocus() Call SetWindowPos(G, t, 0, 0, 0, 0, (SWP_NOSIZE Or SWP_NOMOVE)) The example above makes a Text box "float" on top of a Grid control. --------------- Placing Forms, Controls: Sub InitializeWindow Height = Screen.Height * .75 Width = Screen.Width * .75 Top = (Screen.Height - Height) / 2 Left = (Screen.Width - Width) / 2 ' *** Then place and size your controls. End Sub If you want to make the form a specific size, then Scale it in inches, centimeters, points, or twips (a twip is 1/20th of a point), and don't bother making references to Screen.Height or .Width. Q: My application has a background form and pop-up forms. Sometimes, other windows, such as Program Manager, and even icons, get stuck in the middle between my background and the popups. How can I force some order in this mess? A: In each form's GetFocus, you could use the Floating Window routine above to force each of the forms in your program to the top. --------------- Property Bar Errors: The height read-out in the gray area on the right side doesn't always agree with the Height property. To see this problem in action, try this: 1. Create a text box. 2. Set the Height property to 1385 using the Settings box. The height read-out will now read 1385. 3. Click on the form to unselect the text box. 4. Click on the text box to select it. Now the height read-out says 1335 (it should say 1385). (John Socha) --------------- Resizing Forms: See "Screen Scaling/Form Resizing" under "Screen" --------------- ScaleWidth/Height Purpose: The .Width and .Height properties will give you the external width of the form. The .ScaleWidth and .ScaleHeight properties give you the *internal* dimensions of the form. Keep in mind that the .ScaleWidth and .ScaleHeight values are given in terms of the current .ScaleMode which is usually TWIPS. The good thing is if you change the .ScaleMode to something else, it won't matter because the placement of the controls is based on the Form's .ScaleMode. So, if you want to keep your Picture box at the full area of the form, then you'd do the following in your Form_Resize event: Picture1.Move 0, 0, Form.ScaleWidth, Form.ScaleHeight (Ted Young) --------------- Scrollable Forms: If you need to make a form larger than the screen, you can do so by making it scrollable. Simply put everything in a Picture box and Move it up and down and/or left and right using the Picture1.Top and Picture1.Left properties. Here's a method that uses API calls: Global: Type Rect Left As Integer Top As Integer Right As Integer Bottom As Integer End Type Declare Function ScrollDC Lib "User" (ByVal hDC As Integer, ByVal dx As Integer, ByVal dy As Integer, lprcScroll As Rect, lprcClip As Rect, ByVal hRgnUpdate As Integer, lprcUpdate As Any) As Integer Sub Scroll_Form: MyForm.ScaleMode = 3 'Pixels Dim ScrollZone As Rect ScrollAmt% = -MyForm.TextHeight("1") 'Scroll up 1 line ScrollZone.Left = 0 ScrollZone.Top = ScrollAmt% ScrollZone.Bottom = MyForm.ScaleHeight ScrollZone.Right = MyForm.ScaleWidth Result% = ScrollDC(MyForm.hDC, 0, ScrollAmt%, ScrollZone, ScrollZone, 0, ByVal O&) (Bob Scherer) --------------- Unloading Control Arrays: Q: I am using an array of labels on a drawing to provide visual access to CAD data for a PCB board viewing app. With 50 to 100 labels loaded, I find that exiting my app and restarting it is faster than looping thru and unloading each element before viewing a new drawing. Is there any way to dump and entire control array at once? A: There's no way (besides looping) to delete (unload) all the elements of a control array. However, if you've got 50 or 100 labels you're using too many labels which can *severely* affect performance and availability of system resources for other Windows program. Have you tried simply using the Print statement instead of labels? Instead of doing loading/unloading and/or restarting your app, have you considered simply setting the .Caption property of all the labels to a null string. You might also want to set .Visible=False, depending on if you need all the labels for the new drawing. --------------- Fixed Width Fonts: Courier, System and Terminal are fixed width fonts. --------------- Large Font Bug: If you use a large font and then backspace over it, traces of the text will be left behind. This is a bug in Windows 3.00, by adding a Refresh to the text box (in the _KeyUp event) you can correct the problem. --------------- Listing Printer Fonts: Two problems with listing fonts are that (1) you can get duplicate font names in the list and (2) it is difficult to determine available font sizes. George Campbell offers the following tips: The answer to the first problem is to check the current list of fonts before adding the next one. It's a bit slow, but it works. Use a For...Next loop with the value List.Fontcount -1 to go through the list box and compare the font names with the one you're about to add. The second problem is more difficult. What I do is to simply use an On Error Resume Next line when specifying font sizes. That way, if the font isn't available, the size reverts to the last size selected or to a default size in the case of fixed-size fonts. I suppose you could do that check in a For...Next loop for the sizes you want to test, exiting the loop if an error occurs. Dennis Harrington offers the following tip about duplicate font names: It's documented somewhere in the MSKB that this is a bug is win 3.0. So until win 3.1, we just have to live with it. There's also a little quicker way to parse out duplicates when adding them to a list box. First, concetate all the fonts names into a simple string: test$ = "" 'initiallize a null string numFonts = Screen.FontCount 'Use "Printer" for prt fonts For x = 0 to numFonts 'make string of all font names test$ = test$ + Screen.Fonts(x) Next x For x = 0 to numFonts v = InStr(1, test$, Screen.Fonts(x)) 'find 1st occurance of font z = InStr(v+1, test$, Screen.Fonts(x)) 'look for a 2nd occurance If z = 0 then 'if there are no more occur- ListBox.AddItem Screen.Fonts(x) 'rances of the font, add it End if 'to the list box. Next x Instead of reading the entire list box for each new font (Very slow!), you only need to make 2 very fast InStr calls for each font. --------------- Metafiles in Place of Fonts: If you would like to put your name or your company name in an "about" box, but you don't want to use a VB label (because you can't guarantee the font you want to use will work on other people's machines), and you don't want to use a bitmap, or you want to use "curved" lettering but don't have a fancy graphics program... Well, if you have Microsoft Word for Windows 2.0, you can use its "Word Art" and "Microsoft Draw" mini-apps to create metafiles. You can use Word-Art to create your company/organization name or the program name with fancy features like curved letters, shadows, "button" styles, etc; and then copy it to the clipboard and paste it directly into a VB form or picture control at design time. Best of all, metafiles can be sized from very small to very large without distortion. (Raymond W. Six) --------------- TrueType Fonts Don't All Show Up: Only some of the sizes will be shown in the combo box. But you can click in the edit part of the combo box and type your own point size. (Mike Mezaros) --------------- Using Different Fonts, Colors in a Box: The only way to have different colors and fonts in a box is with the .Print method in a Picture Box or on the Form itself. No other VB boxes allow different colors and fonts. --------------- System Font: ALERT: In a List box using the System font, 9 pitch is non-proportional, 9.75 is proportional. --------------- Calling Form_Paint: You can call Form_Paint from anywhere within the same form as the _Paint method. The only thing you have to know, which isn't documented, is that you have to use the word "Form" and *not* the actual name of your form. I use Form_Paint all the time. (John Socha) --------------- Changing Form Name: To change the name of a form (not the form's FILE name), select the form and pick FormName in the Properties list. This is often overlooked, for some reason. --------------- Detecting a Form's Getting Focus: The following will detect when a form gets focus: Sub Main hMyTask = GetCurrentTask() hOldTask = hMyTask Form1.Show Do Do Ok = DoEvents() hTask = GetWindowTask (GetActiveWindow) Loop Until hTask <> hOldTask If hTask = hMyTask then Form1_GotFocus Else Form1_LostFocus End If hOldTask = hTask Loop End (J.Zuck) --------------- Flashing a Form: Declare Function FlashWindow% Lib "user" (ByVal hWnd%, ByVal bInvert%) Command1_Click Randomize z& = BackColor For i = 1 to 12 FlashColor% = Int(7 * Rnd + 1) BackColor = QBColor(FlashColor%) FlashIt% = FlashWindows(hWnd, 1) For j = 1 to 20: Next Next RestoreIt% = FlashWindows(hWnd, 0) BackColor = z& 'from Mike Mezaros' "Executive Decision Maker" --------------- Focus & Order of Execution: Some times VB will delay doing something while it executes more code. For example, if you write a lot of stuff to a List box in a tight loop, the List box display on the screen will not be shown updated until the code comes to a "resting point" (ie: waiting for user input). --------------- Focus on StartUp: If you try to display a form on startup that has your logo, copyright notice, etc, you may find that the form displays, but the detail on it does not. You have to force VB to wait until the detail is displayed. One way to do that is do define a Global variable and when you are finished with the Title box, set the variable ("Continue", in the example below) to true (-1). Form1_Load: TitleForm.Show Do x = DoEvents() 'allow control to the system Loop Until Continue = -1 Another way is to "Loop Until Len(Label1.Caption) > 0" (You can omit the >0.) This assumes you have a Label1 box, of course. Another problem is trying to get some other form to load and to transfer focus to it during start-up, and then continuing the start-up. What happens is that you can do a Form2.Show, for example, but after the Form2_Load code (if any) is run, focus will immediately return to the original form. Users don't get a chance to interact with Form2. If Form2 is an installation form, for example, the installation will never get done. The solution is to use Show Modal (eg: Form2.Show 1), with the possible drawback being that you have to Unload the form to return to the calling form. --------------- Form.GotFocus: GotFocus will not be executed for a Form that has a control on it. --------------- Modal Forms, Unloading: You can perform a Form.Hide in the modal form and then unload it when you return back to the calling form. --------------- SetFocus Won't Work?: You cannot do a SetFocus until a form is visible. Use Show instead. --------------- Title Bar, Hiding: If you set the ControlBox, MinButton, and MaxButton properties to false then setting the Caption property of the form to "" (no spaces), you will get a border but no title bar. This works with any borderstyle other than "none". --------------- Unloading Forms Completely: If you have some reason to suspect that your VB apps aren't exiting as completely and cleanly as they are supposed to, you may have a hidden form lurking around. Make it a point in the main form's Unload proc to unload all other forms associated with the app: Sub Form_Unload(Cancel As Integer) Unload Form2 Unload Form3 ' etc. End Sub --------------- Unloading Forms From Task Manager: Form_Unload will not be called if you exit a VB app from the Task Manager. MS is aware of this problem. --------------- Unloading Forms on Windows Termination: If Windows is about to terminate, it will invoke your Form_Unload procedure. In that proc, you can do whatever you want, including telling Windows to *cancel* the shutdown! To cancel the Form_Unload, use: Cancel=True. It is reported that during a Windows Termination shutdown of your app, if your Form_Unload tries to unload other forms, you will get a UAE. A work-around is to put a timer on all forms that stay loaded other than my main form. The event for that timer is Form_Unload for the form it is on and the design time interval is 0 (timer off). The Form_Unload for the main form can change the timer interval on those forms and unload them without the UAE while exiting. (Barry Simon) The file RANDGR.EXE on DL6 shows how to manually scroll data through a Grid. --------------- What is the Grid Control?: The Grid Control is like a List Box divided into columns. It is not part of VB, but is in the Professional Toolkit and other 3rd-party add-on's. --------------- Alternative to a Grid Control: For anything other than a very simple grid-type display, it makes more sense to buy a third-party grid control. The one in VBTools is superior to the one in the MS Professional Toolkit, but the PTK is cheaper. For very simple grid-type displays, here is an example of how to make your own: Sub Form_Paint () Line (100, 100)-(2500, 2500), , B For XPos = 100 To 2100 Step 400 Line (XPos, 100)-(XPos, 2500) Next For YPos = 100 To 2100 Step 400 Line (100, YPos)-(2500, YPos) Next End Sub Sub Command1_Click () i$ = InputBox$("Enter X and Y coordinates separated by a space", "") XCoord% = Val(Left$(i$, 1)) YCoord% = Val(Right$(i$, 1)) XCoord% = 100 + ((XCoord% - 1) * 400) YCoord% = 100 + ((YCoord% - 1) * 400) Line (XCoord%, YCoord%)-Step(400, 400), RGB(0, 0, 0), BF End Sub (Ian Taylor) --------------- Can't Enter "&": You cannot put a single "&" as text in a cell of a Grid. You have to put "&&". --------------- Graphics in a Grid Cell: You cannot draw directly to a cell in the PTK's Grid control. Instead, draw to an invisible Picture box then assign the image to the the Grid cell: Picture1.Autoredraw = -1 ' draw stuff on Picture1 Grid1.Picture = Picture1.Image The speed issue can be addressed by building the graphs one by one, and transferring to a piclip control via an ordinary control. Then load the grid in a loop from the piclip. You redraw the next set while the user sees the old. THe following loads in 4 seconds: grid1.rows = 6:grid1.cols = 8 For i = 1 To 6 grid1.rowheight(i - 1) = 1000 Next i For i = 1 To 8 grid1.colwidth(i - 1) = 1000 Next i grid1.width = 8.4 * grid1.colwidth(1):grid1.height = 6.4 * grid1.rowheight(1) picture1.Move form1.width - picture1.width, form1.height - picture1.height, grid1.colwidth(1), grid1.rowheight(1) picture1.autoredraw = -1 'THIS IS IMPORTANT. The picture property of graph1.drawmode = 3 'graph does not contain the bitmap so picture1.picture = graph1.picture 'use VB and autoredraw to create and picture1.picture = picture1.image 'then assign to the picture property For i = 1 To 5 For j = 1 To 7 grid1.row = i grid1.col = j grid1.picture = picture1.picture Next j Next i (Robert Eineigl) --------------- Faster Row Adding/Deleting: Inserting a blank line (or deleting a line) in a GRID has always been slow. If you have a Grid with 300 rows, and you wanted to insert a new line at row 10, you'd have to move 290 rows down, one at a time. A MUCH faster way is to define the block of rows to move using .SelStartRow and .SelEndRow properties, .Clip it, then select the new block (which begins one row down from the previous block) and .Clip it again! It reduced the time required to do an insert by 90%. InsertRow% = Grid.Row 'insert a new row at the current row. LastRow% = Grid.Rows 'last row in the grid. Grid.Rows = LastRow% + 1 'since we are inserting a new row, our Grid 'is one row larger than before. Grid.SelStartRow = InsertRow% 'select the block that we are going to Grid.SelEndRow = LastRow% 'move down. Grid.SelStartCol = 0 'in this example, we've got 9 columns Grid.SelEndCol = 8 'in the grid. Stuff$ = Grid.Clip 'put the contents of the selected rows 'and columns into Stuff$ Grid.SelStartRow = InsertRow% + 1 'define a new block which starts one Grid.SelEndRow = LastRow% 'row down from the previous block. Grid.Clip = Stuff$ 'and stuff the old block into the new one. (Jim Dolson) --------------- Changing Icons: You can select from a set of icons, using Form1.Icon = ... . You can also draw on the icon exactly as if it were a normal-sized form by setting Form1.Icon = LoadPicture() Then you can draw on it with normal drawing statements (Line, Circle, etc). --------------- Getting an Icon from an EXE: To extract an icon from an EXE file 1. Get the handle to the EXE using FindWindow (assuming its up and running) 2. Plug the handle into GetWindowWord with the constant GWW_HINSTANCE to get an hInstance. 3. Feed the hInstance into LoadIcon to get an HICON (handle). 4. Now assuming you have a DC from a picture control or form already, then use that in DrawIcon. Global.Bas: Declare Function GetWindowWord Lib "User" (ByVal hWnd As Integer, ByVal nIndex As Integer) As Integer Declare Function LoadIcon Lib "User" (ByVal hInstance As Integer, ByVal lpIconName As Any) As Integer Declare Function DrawIcon Lib "User" (ByVal hDC As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal hIcon As Integer) As Integer Declare Function FindWindow Lib "User" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Integer ' Window field offsets for GetWindowLong() and GetWindowWord() Global Const GWW_HINSTANCE = (-6) Form1_Declare: Dim hInstance, handle, HICON As Integer Form1_Click: 'handle = FindWindow("TheAPP'sClass", "TheApp'sWindowname") handle = FindWindow("Progman", "Program Manager") hInstance = GetWindowWord(handle, GWW_HINSTANCE) Do HICON = LoadIcon(hInstance, n&) n& = n& + 1 Loop Until HICON <> 0 Picture1.Autoredraw = -1 If DrawIcon(picture1.hDC, 10, 10, HICON) Then Print "OK" Picture1.Refresh Also note that DrawIcon is also in User not GDI. (Robert Eineigl, MS) --------------- Icon Caption Doesn't MOVE: If you use the Move method on your form while it's minimized, the icon will move as you would expect it to, but the caption doesn't move with it!!! I used Spy to determine that the caption is a separate Window (with a style name of "#32767"). (Mark Novisoff) As long as you don't have the .Move code in the _Resize event, you can force the caption to catch up by simply setting WindowState = 1. That will force a kind of Refresh, bringing the caption into place. (Jonathan Zuck) --------------- Icon - Get Rid Of: 1. Click on the form 2. Select the Icon property from the properties bar. 3. Click on the middle portion of the properties bar, where (icon) shows. 4. Press the Del key --------------- Transparent Background: If you create an icon with Icon Works (comes with VB), the area behind the icon may be colored in. If you try to do animation with an icon, you will find that although the background on the icon may be transparent, the Picture control is not, so you see a block moving across the screen. To make the background invisible, use IconWorks' "screen inverse" setting and paint the background. (Marshal Bostwick) --------------- An Alternative to Label Boxes: You can save system resources by printing directly on a form instead of using a Label control: LabelText$ = "Some text" e = FormName.TextWidth(LabelText$) FormName.CurrentX = TextControl.Left - e - (the amount desired for spacing) FormName.CurrentY = TextControl.Top (+ or - the desired spacing) FormName.Print LabelText$; (Dennis L. Harrington) You can print 3-D or shadowed text on a form as follows: Picture1.ForeColor = &HC0C0C0 'light gray shadow Picture1.CurrentX = 70 'vary depth to taste Picture1.CurrentY = 70 Picture1.Print "This is shadowed or 3D text." Picture1.ForeColor = &H0& 'black Picture1.CurrentX = 50 'vary location to taste Picture1.CurrentY = 50 Picture1.Print "This is shadowed or 3D text." --------------- Captions, Multi-Line: You can make a label caption multi-line simply by inserting a CHR$(13): Label1.Caption = "This is Line 1" + Chr$(13) + "This is Line 2" This can only be done with code during runtime, not design-time. During design time, you can force text down to the next line by inserting enough spaces, but this may not stay the way you want it on different types of monitors. This only works with Label boxes, not Button captions; however, you can easily emulate Button controls with Picture boxes and put pretty much whatever you want on them. (Nelson Ford) --------------- Label Box Text Background Color: With some light backgrounds for a label box, the background behind each individual letter in the box is a different (darker) color. This is a "feature" and nothing can be done about it. --------------- "AddItem #" Error: You will get an error that may be hard to figure out if you do a List1.Additem x$, n and "n" is a value greater than ListCount. --------------- Bitmaps in List Boxes: To display a bitmap in a list box: 1) Load a bitmap into a picture control, and set it's AutoRedraw to True 2) Set the Left property to a negative number so it resides off the screen. 3) Put the following code in the Form_Paint event sub: Success = BitBlt (List1.hDC, DestX, DestY, XPixels, YPixels, Picture1.hDC, 0, 0, SRCCOPY) Where: List1 is the name of the list box control. DestX is the X coordinate inside the List box. DestY is the Y coordinate inside the List box (both X and Y in Pixels) XPixels is the width (in pixels) of the bitmap YPixels is the height (in pixels) of the bitmap Picture1 is the name of the picture control holding the bitmap 0 and 0 are the X/Y coordinates within the picture (0,0)=top/left SRCCOPY is a windows constant &HCC0020 which tells BitBlt to copy the bits verbatim. Here is the syntax for declaring BitBlt in VB: Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As I ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc As Integer, ByVal dwRop As Long) As Integer (Carl Franklin) --------------- Capacities: The List Box should handle strings up to around 1K in length with a total size around 64K. Your mileage may vary, as Bill Reilly discovered: Combo boxes and List boxes will only accept up to 5440 lines. Try this: For i = 1 to 10000 List1.AddItem "x" Next It will bomb out at i = 5440 --------------- Clear a List Box: The following routine will quickly clear a list box: Global: Declare Function SendMessage% Lib "user" (ByVal hWnd%,_ ByVal wMsg%,_ ByVal wParam%,_ ByVal lParam&) Declare Function GetFocus% Lib "user" () Declare Function PutFocus% Lib "user" Alias "SetFocus" (ByVal hWnd%) Form Declarations: Const WM_USER = &H400 Const LB_RESETCONTENT = WM_USER + 5 Sub ClearListBox (Ctrl As Control) hWndOld% = GetFocus() Ctrl.SetFocus x = SendMessage(GetFocus(), LB_RESETCONTENT, 0, 0) Suc% = PutFocus(hWndOld%) End Sub --------------- Finding an Item Added to a Sorted List Box: When you AddItem to a Sorted List Box, the lines displayed in the List Box do not automatically change so that the new item is displayed. You can search for the new item manually (see next Tip) or instead of using List.AddItem, use the following: SendMessage(hWnd, LB_ADDSTRING, wParam, lParam). This adds the item to the list and returns the ListIndex of the newly added item. Here is the complete routine (from Bob Scherer): In the Global module, declare the following constants & functions: Global Const WM_USER = &H400 Global Const LB_ADDSTRING = (WM_USER + 1) Declare Function GetFocus Lib "user" () As Integer Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As Integer, (all this on one line)--> ByVal wParam As Integer, ByVal lParam As Any) As Long In the procedure where you want to do the "AddItem" use this code: List1.SetFocus LbhWnd = GetFocus() RetVal& = SendMessage(LbhWnd, Lb_AddString, WParam, MyInput$) List1.ListIndex = RetVal& --------------- Finding an Item Manually: (Also see the preceding and following Tips.) The following routine uses a binary search to find an item in a sorted List box: Object: (general) Proc: FindItem Sub FindItem (Lst as Control, a$) U = Lst.ListCount L = 0 Do If U < L Then MsgBox "Not Found" Lst.ListIndex = I 'set .ListIndex to nearest match Exit Sub End If I = (L + U) / 2 If a$ = Lst.List(I) Then Lst.ListIndex = I 'Found. Set ".ListIndex" accordingly Exit Sub ElseIf a$ > Lst.List(I) Then L = I + 1 Else U = I - 1 End If Loop (Nelson Ford) --------------- Finding an Item via API: The following routine will search a List box, sorted or unsorted, for the first occurrence of a specified string. Declare Function SendMessage& Lib "user" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, ByVal lParam As String) Declare Function Getfocus% Lib "user" () Dim ListHwnd As Integer Sub Form_Click () List1.AddItem "John" List1.AddItem "George" List1.AddItem "Sue" End Sub Sub Text1_Change () Const LB_SELECTSTRING = &H400 + 13 Static X As Long wParam = 1 lParam$ = Text1.Text + Chr$(0) X = SendMessage(ListHwnd, LB_SELECTSTRING, wParam, lParam$) Print X End Sub Sub Form_Load () Form1.Show List1.SetFocus ListHwnd = Getfocus() End Sub (Don Funk, MS) Note that LB_SelectString will NOT get the index of a string specification if it is more than the first word. (eg: finds "John" but not "John Smith".) (Robert Werning) --------------- Font Size Changes List Box Size: If you change font sizes during runtime, VB will change the size of the List Box to adjust it so that partial lines are not shown. This causes an unwanted change in your display, and VB does not let you disable this feature. The ratio between 8.25 print size and 9.75 is around 1.2 and if you set the number of lines with this ratio in mind, the number of lines displayed can change without changing the size of the List Box. For example, 6, 11, or 22 lines of 8.25 text will convert to 5, 9, or 18 lines at 9.75. (Keith Funk) --------------- Forcing an Item Number to the Top: To force a particular item number in a List Box to be at the top of the box, enter the following two lines: List1.ListIndex = List1.ListCount - 1 List1.ListIndex = ItemNumberWanted If you do not enter the first line, the line you want will be in the List Box, but not necessarily at the top of it. --------------- Inhibiting Screen Updating: If you are adding a whole bunch of items to a list box all at once like assigning items from an array from within a loop, you might want to use the API WM_SETREDRAW message to prevent the list box from being continually updated on the screen as each item is added. Not only does this stop the flickering, but it makes adding the items much faster. A simpler method is just set the List box's Visible property to 0 and reset it to -1 when done, although this will cause the List box to disappear completely. --------------- Linking Sorted and Unsorted List Boxes (or Arrays): You may need to link data in an array or an unsorted list box to data in a sorted List box; however, when an element is added to a sorted List box or if you manually sort an unsorted List box, the array or related List box will no longer be in sync with the sorted List box.. There are several ways to keep the two in sync: One way is to use the routines under "Finding an Item...", above, and then adjust the unsorted array or List box accordingly. The file VBSORT on DL6 illustrates how to sort a List box and an array at the same time. Another way is to add a pointer at the end of each item in the sorted List box. By tabbing over far enough, the pointer will not show up in the List box. This method is not ideal if the contents of the List box will be approaching the 64k space limit, since adding a pointer takes up space in the List box. A third way is to append a 4-byte value to each item in a List Box by using the LB_SETITEMDATA message and you can retrieve the value using the LB_GETITEMDATA message. The tag is created when you add an item to the List Box. The value of the tag can be retrieved at anytime. This approach requires a good deal of familiarity with API calls. --------------- "ListIndex = -1" Generates Click Event: If you use "List1.ListIndex = -1" to unselect a highlighted item in a List box, a (probably unwanted) click event will be generated. To negate the unwanted click action, add the following line to List1_Click: If List1.ListIndex = -1 Then Exit Sub (Selden McCabe) --------------- Selecting Multiple Items: The VB List Box does not directly support selecting multiple items. MULTIPK is a sample project that shows how to create a VB List box that DOES allows selecting multiple items. Look for it in the MSBASIC forum or in PsL. --------------- Tab Stops: Q: I have downloaded App Note Q71067 on "How to Set Tab Stops Within a List Box". The sample code in the note works fine, but if I change the lengths of the quoted elements in the list, the example falls apart. It appears that there are pre-existing tab stops *between* the stops I create! Does anyone know if this is, indeed, true? And if so, how do I clear the preexisting tab stops before I load in my own. A: The LB_SETTABSTOPS does not use character units, but a Dialog unit based on the currect system font width and height. This means you may have to adjust the tabstop values to a larger size depending on the fontname and fontsize. Try increasing the values and use a mono-spaced font to get an idea of how it works, it is a pain! --------------- Tab Stop Setting: Declare Function GetFocus Lib "user" () As Integer Declare Function SendMessage Lib "user" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wp As Integer, lp As Any) As Long Declare Function APISetFocus Lib "user" Alias "SetFocus" (ByVal hWnd%) As Integer Const WM_USER = &H400 Const LB_SETTABSTOPS = WM_USER + 19 Sub Form_Click () Static tabs(3) As Integer List1.SetFocus lbhWnd% = GetFocus() tabs(1) = 10 tabs(2) = 50 tabs(3) = 90 retVal& = SendMessage(lbhWnd%, LB_SETTABSTOPS, 3, tabs(1)) List1.AddItem "Name" + Chr$(9) + "Rank" + Chr$(9) + "Serial#" List1.AddItem "J. Doe" + Chr$(9) + "O-3" + Chr$(9) + "1234" List1.AddItem "J. Blow" + Chr$(9) + "E-1" + Chr$(9) + "5678" End Sub (Jeff Simms) --------------- AppName.Caption Problem: Page 118 of the Programmer's Guide shows how to dynamically adding items to a menu. About 3/4ths of the way down the page, it shows a routine with the last line being: AppName(LMenu).Caption = FileSpec$ If you follow this example, you will get another separator bar. To get the FileSpec$ to show, you must add the following line: AppName(LMenu).Separator = 0 --------------- Calling Help for Menu Items: It would be nice to be able to call the Windows Help Engine from a menu where, if a user has a menu selection highlighted and hits F1 or Sh-F1 then help will come up for that menu item. Here's how to detect those keypresses: 1. Global - declarations Global HelpTopic% ' Also, set up some constants to use for help topics here. It will ' make your program much more readable 2. In each menu item's Click proc: Sub Ctlname_Click ' You can also sprinkle statements like this in appropriate ' areas of your code if you like. HelpTopic% = One_of_the_CONST_items End Sub 3. In each control's keydown proc: Sub CtlName_Keydown(.....) CheckHelp Shift, KeyCode End Sub 4. In a module (or the form, if it's a single form program) Sub CheckHelp(Shift%, KeyDown%) If Shift% = DesiredShiftStatus and KeyDown% = HelpKey Then ' Invoke WINHELP here, passing HelpTopic% End If End Sub (Mark Novisoff, MicroHelp) --------------- ClipBoard Status: If you have a menu set like the following: Edit Cut Paste Copy and you want to gray out Paste and Copy when the clipboard is empty, then check the clipboard's contents as soon as "Edit" is clicked and modify the enabled property of the Paste and "Copy selections accordingly. --------------- Disabling Menu Items: There's an example in the VB manual that shows how to enable and disable menu items as a menu is opened. Let's say I have a "File" Menu with CtlName FileMenu, and "Open" and "Save" items on that menu with CtlNames of FileOpen and FileSave respectively, and I want the "Save" Menu item to be Enabled or disabled depending upon whether a file is currently open. As an extension of the example in the manual, I might intercept the FileMenu_Click event, then set the FileSave.Enabled property. This method is acceptable, unless there is an accelerator key associated with the FileSave menu item. In that case, the FileSave.Enabled property may not have be set correctly when the user presses the shortcut key, unless the FileMenu has been opened in the mean time. (Steve Schmidt) --------------- Focus Doesn't Change: If you choose a menu item that executes another procedure, the control that had focus before the menu selection never loses it; that is: LostFocus for that control is not executed before the menu's procedure is executed. You can manually trigger it if you know which control had focus by entering, for example, Call Text1_LostFocus. You can determine which control had focus by setting up a set of flags to keep track of each control. For example: Global Command1Focus Command1_GotFocus Command1Focus = -1 --------------- Right Justify "Help": In Windows 3.0 menu bars, "Help" is right-justified. In past VB-Tips, we had some complex ways to right-justify your own Help menu heading, but it turns out that you can do it with one line of code added to the Load procedure: Help.Caption = Chr$(8) + Help.Caption This came from a suggestion by Michael Partsch. His suggestion included setting up all menu headings as an array, but a quick test showed that the array is not necessary. It should be noted that the preferred design in Windows 3.1 is NOT to right justify Help. --------------- Screen Flicker on Selection: Q: I have a VB app whose main window has a variable menu. I make some of the entries visible or invisible depending upon circumstances. The problem I am encountering is the amount of screen flicker when this menu change occurs. Dropping one item and adding another on the menu bar causes a lot of flicker on the whole screen, and takes display time to boot! A: You can set the .Visible property of the "main" item to 0, update the menu, then set .Visible back to -1. --------------- An Alternative to Picture Boxes: Picture boxes eat up a lot of system resources. An alternative is to put the picture directly on the Form rather than using a Picture box. The code is fairly simple and straightforward, as shown in the example below which is used to perform animation on the About form in one of my apps: 'put in your Declares: DefInt A-Z Declare Function BitBlt Lib "GDI" (ByVal hDestDc, ByVal x, ByVal y, ByVal nWidth, ByVal nHeight, ByVal hSrcDC, ByVal XSrc, ByVal YSrc, ByVal dwRop As Long) 'all on one line, of course Global Const SrcCopy& = &HCC0020 'animation sub: Fsm = Form1.ScaleMode Form1.ScaleMode = 3 Picture1.ScaleMode = 3 Picture1.AutoRedraw = -1 SrcCopy& = &HCC0020 For x = Form1.scalewidth To (Form1.scaleleft - Picture1.scalewidth) Step -1 ret = BitBlt(Form1.hdc, x, y, Picture1.Width, Picture1.Height, Picture1.hdc, 0, 0, SrcCopy&) Next Form1.ScaleMode = Fsm The above simply moves the picture from the right of the form to the left. "x" and "y" are the locations on the form. Picture1 is located on out of sight on the form. Multi-cell animation can be done by using multiple pictures. To place a fixed image on a Form, just eliminate the loop, of course. (Nelson Ford) You can also conserve resources by combining pictures into one Picture1 box and BitBlt-ing the desired form to the Form or another Picture box. The following code by Jeff Simms shows how to use a combined set of 9 pictures as a tool bar from which the user can select by clicking the mouse: Picture1 (autoredraw false) stores a bitmap with all the tool icons (9). Picture2 (autoredraw true), twice the height of the first one, stores a bitmap with all the Up tool icons on the top half of the bitmap and all the Down tool icons on the bottom half. After setting the scalemode for both boxes to pixels, I used the following code to transfer the DOWN tool icon to Picture1: Sub Picture1_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single) N = Int(X / (Picture1.ScaleWidth / 9) + 1) 'icon selected Z = Picture1.ScaleWidth / 9 'width of icon L = Z * (N - 1) + N 'X for BitBlt - 1 pixel adjustment per icon I = BitBlt(Picture1.hdc, L, 0, Z, Picture1.ScaleHeight, Picture2.hdc, =>L, Picture2.ScaleHeight / 2 + 1, SRCCOPY) '1 pixel adjustment for YSrc End Sub You can also use BitBlt with DSTINVERT and just one picture box You can easily combine graphics into a single Picture box by first laying them out next to each other on a form using separate Picture boxes, then use PrintScreen to capture them and Paintbrush to clip and save them. --------------- As Buttons: You can use a Picture Box to create a button with whatever graphics you want on it. However, the Click event code will only be executed only if the mouse cursor is still over the control when it is released. This could cause problems. --------------- AutoRedraw: BitBlt works faster with AutoRedraw OFF. The problem is that SavePicture Picture1.Image only works with AutoRedraw ON. A solution is to leave AutoRedraw Off while you do your drawing and then use the following code to BitBlt the non-persistent image that you just created to the persistent image. Note that AutoRedraw is OFF when this code executes. ScrnhDC = Picture1.hDC 'Handle to non-persistent image. Picture1.AutoRedraw = True PersistenthDC = Picture1.hDC 'Handle to persistent image. 'BitBlt from ScrnhDC to the PersistenthDC. Picture1.AutoRedraw = False In addition, I sometimes follow this with Picture1.Picture = Picture1.Image. This gives me three different levels of 'protection'. Picture1.Cls with AutoRedraw OFF erases the non-persistent image only. Picture1.Cls with AutoRedraw ON erases both the persistent and non-persistent images, but preserves the Picture Property image. And Picture1.Picture = LoadPicture() zaps all three images. This works great for graphics created by VB graphics commands and the API PolyLine Function. - KeithF --------------- BitBlt to/from Hidden Box: If you want to BitBlt from/to a non-visible picture control, make sure the AutoRedraw property is set to TRUE. --------------- "Box" Command: There's no explicit BOX command, but it's part of the LINE syntax. Check out the manual for the syntax (pg 172). --------------- To Clipboard as Metafile?: Q: How I copy the contents of the picture box to the clipboard as a metafile? Just using the metafile parameter doesn't work. A: The graphic methods create BMPs, not Metafiles. --------------- Copying a Drawing to Clipboard: The following will work, IF Picture1.AutoRedraw is True. (J. Zuck) Sub Picture1_Click () ClipBoard.Clear Picture1.Circle (1000, 1000), 500 ClipBoard.SetData (Picture1.Image) 'Picture1.Picture won't work. End Sub Sub Picture2_Click () Picture2.Picture = ClipBoard.GetData() 'Picture2.Image is illegal. End Sub --------------- Copying Text in a Picture: If you use the Print command ("Picture1.Print...") to print text in a Picture box, that text will NOT be copied if you use a command like "Picture2.picture = Picture1.picture". You have to re-print the text in the second box. --------------- Cutting-Pasting to a Picture Box: Fm: Don Funk (SL) 76701,155 The code that follows demonstrates how to place the pawn from the CHESSS.BMP (that comes with Windows) into a picture control. 1. Create the following controls using the Properties Bar: CntlName Height Left Top Width Form1 4920 7305 Picture1 2295 360 4440 2535 Picture2 2415 4080 1560 2535 Command1 855 3600 240 1695 Form1.Picture = Picture("c:\win\chess.bmp") Picture1.Visible = FALSE Picture1.AutoRedraw = TRUE Picture1.scalemode = PIXEL Picture2.scalemode = PIXEL 2. Enter the following code: Global.bas: (Get the Declare Function BitBlt Lib from "Saving Picture Files" further down in the VB-TIPS file.) Sub Command1_Click () '* Handle for the destination bitmap. hDestDC% = Picture1.Hdc '* X% & Y% coordinates of the invisible bitmap X% = 0: Y% = 0 hSrcDC% = Form1.Hdc '* Specified X1 & Y1 coordates of the bitmap to clip. XSrc% = 80 YSrc% = 25 '* Specify the dimensions of the bitmap to copy. nWidth% = 110 nHeight% = 110 '* Specify source copy{SRCCOPY} dwRop& = &HCC0020 '* Copy section of the bitmap to the hidden picture control. S% = BitBlt%(hDestDC%, X%, Y%, nWidth%, nHeight%, hSrcDC%, XSrc%, YSrc%, dwRop&) '* Copy to the clipboard. clipboard.SetData Picture1.Image, 2 '* Copy to the Picture2 control. Picture2.Picture = clipboard.GetData(2) End Sub --------------- Dragging A Full Picture: Q: How can I drag a picture control whole, instead of just the outline, and still have all the drag-and-drop functions? A: Sorry, you can't have both. See the program Accordian Solitaire in the file listing for an example of dragging and dropping. --------------- Dragging Text Box in Picture: The following is code by Don Funk for dragging a Text box in a Picture box: Declare Function SetParent% Lib "user" (ByVal H%, ByVal H%) Const DRAG_START = 1 Const DRAG_STOP = 2 Sub Picture1_DragDrop (Source As Control, X As Single, Y As Single) Print X, Y Source.SetFocus Child% = GetFocus() Picture1.SetFocus NewParent% = GetFocus() X = X - Text1.Width / 2: Y = Y - Text1.Height / 2 H% = SetParent(Child%, NewParent%) W% = DoEvents() Text1.Drag DRAG_STOP Source.Move X, Y End Sub Sub Form_DragDrop (Source As Control, X As Single, Y As Single) Print X, Y Source.SetFocus TextHwnd = GetFocus() H% = SetParent(TextHwnd, Form1.Hwnd) X = X - Text1.Width / 2: Y = Y - Text1.Height / 2 Text1.Drag DRAG_STOP Source.Move X, Y End Sub Sub Command1_Click () Text1.Drag DRAG_START End Sub --------------- Drawing - Scale: You can change the scale for pictures from twips to virtually anything you want (100 allows you to do easy percent drawing), but you still have to draw by using pixel measurements. I found this out by trying to set DrawWidth to draw a line covering the middle 20% of a small picture. --------------- Faster Drawing: You can speed up drawing by having a hidden (.Visible=FALSE) picture box which has AUTODRAW=TRUE. You echo your output to this box but the autodraw does not slow things down because it is hidden. Then, in the paint method of your visible picture box, blast the image from the invisible box to the visible box. The whole thing is *very* fast and preserves your work. - J. Zuck If the drawing is slow and complex, then bitblt is the best way. You won't need a hidden picture control to bitblt to as you already have two additional images available that are part of any picture control. They are the persistent image (that AutoRedraw uses) and the Picture Property. Leave AutoRedraw OFF. Draw on the 'screen' image of the picture control (non-persistent image) then bitblt the screen image to the persistent image. This is a way of turning AutoRedraw ON after you have drawn the image. It makes for fast drawing and a way to protect what you have drawn. You can get the hDC of the persistent image by turning AutoRedraw ON, using hDC and then turning AutoRedraw OFF. You also have available another level of protection. You can copy the persistent image to the picture property (ctl.picture = ctl.image). The picture property doesn't get erased even if you use CLS with AutoRedraw ON. - Keith Funk The following is some actual code for the above: 1. Picture.AutoRedraw = 0 2. do the .Line statements to draw the stuff 3. Now copy the "screen" image to the AutoRedraw "persistant" image: hScreenDC = Picture.hDC ' get the device context handle of the "screen" Picture.AutoRedraw = -1 ' set it to true, so the hDC points to .Image PrevScaleMode = Picture.ScaleMode Picture.ScaleMode = 3 ' pixels r%=BitBlt(Picture.hDC,0,0,Picture.ScaleWidth,Picture.ScaleHeight, hScreenDC,0,0,SRCCOPY) Picture.ScaleMode = PrevScaleMode Make sure to type the "r%=BitBlt..." all on one line, and don't forget the declaration for it (and the const SRCCOPY) in your Global code module. You can get those declarations from the WINAPI.TXT (stored as WINAPI.ZIP) if you don't already have it. Also, if you're using a custom ScaleMode (by setting the ScaleTop/Left/etc. yourself), then you'll have to somehow convert the measurements to Pixels yourself because BitBlt _requires_ the parameters to be in Pixels. (Ted Young) Other than the above, the only real way to spead things up is to limit the number of pens that are used (a pen includes the color of the line and the width) and use API calls to draw the lines. VB does all this dynamically so it's slower. (J. Zuck) If you set AutoRedraw = -1, Visual Basic transfers the image to the screen only during idle periods, so if you're drawing several thousand points, this can seem quite slow. I have a program that draws 3000 lines in about 3 seconds on my computer, which seems like a lifetime. But you can make this "feel" faster with a simple trick. If you call the Refresh method periodically, you can provide feedback while you're drawing without slowing down the program much. For example, my program that draws 3000 lines looks something like this: Dim i As Integer pic.CurrentX = points(1).x pic.CurrentY = points(1).y For i = 2 To numPoints pic.Line -(points(1).x, points(2).y) If i Mod 500 = 0 Then pic.Refresh End If Next i This will feel faster because your picture will be redrawn every 500 points, or about every 1/2 second (on my 25 MHz 386). By the way, Polyline is about 5 times faster, but you must work with Pixel coordinates. More notes: 1. Calling Refresh rather than DoEvents tells Visual Basic just to transfer your new image to the screen. It won't process any other events, which can be good or bad. The good part is that Refresh may be faster. The bad part is that you can't have a cancel button to stop long drawing operations. 2. Here's how you can set the redraw so it happens after a certain amount of time has ellapsed (without using a timer object): Dim t As Single ' Timer returns a Single t = Timer ' Get current clock ticks For i = 1 To numPoints ' Draw all the lines pic.Line ... ' Draw a line If (Timer - t) > 0.5 Then ' Has 1/2 second passed? pic.Refresh ' Copy new image to screen t = Timer ' New starting time End If Next i This example updates your image every half second. As you can see, it's almost as simple as using a Timer object, but you don't have to call DoEvents. (John Socha) --------------- Number of Colors Supported: VB only supports 16-color graphics, even if your system supports 256-colors. This is well-documented in the manuals, but a lot of people ask about it. --------------- Saving Extracted Icons: Q: I have used the ExtractIcon function from shell.dll (WIN31) to extract icons from *.EXE and *.DLL's. The icons are displayed in a Picture control. I can't save the resulting picture. It seems that VB believes the picture control has a "none" property, perhaps due to the fact the the API calls filled the control. A: Put "PicName.Autoredraw = TRUE" and do a "PicName.Picture = PicName.Image" after you load the icon. --------------- Saving Picture Files: It seems that SavePicture saves everything when the property is a Picture on a Form, but it saves only the original loaded file when the property is a Picture on a PictureBox, and it saves only the new drawn lines when the property is an Image in a PictureBox. The following should fix it: 'BMP or WMF loaded into Picture1 'Redlines added with Line, Pset, etc., then Form1.Picture = Picture1.Image PictureSave Form1.Image, "TEST1.BMP" This works with WMFs but they cannot be changed and saved as WMFs, they get saved as BMPs, which may or may not be ok with you. Another example was posted: Sub SavePicture_Test () Picture1.Picture = LoadPicture("chess.bmp") Form1.Picture1.Line (1, 1)-(1000, 1000) SavePicture Picture1.Image, "test.bmp" }"Image" not "picture"! Form1.Picture1.Picture = LoadPicture("test.bmp") End Sub More input on the subject: 1. Set AutoRedraw to True, and use the .Image property in the SavePicture statement. 2. If you don't want to use the AutoRedraw property (since it is slower when it's True than when it's False), use the following code before your SavePicture hScreenDC = Picture1.hDC 'get the device context handle of the "screen" Picture1.AutoRedraw = -1 'set it to true, so the hDC points to .Image r%=BitBlt(Picture1.hDC,0,0,Picture1.ScaleWidth,Picture1.ScaleHeight, hScreenDC,0,0,SRCCOPY) Picture1.AutoRedraw = 0 'set it back to normal SavePicture Picture1.Image,"test.bmp" 'save the image with all changes Note: type the "bitblt" statement all on one line, and don't forget to include the following in your Global.Bas code form: 'type the following all on one line (this is taken from WINAPI.TXT) Declare Function BitBlt Lib "GDI" (ByVal hDestDC As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hSrcDC As Integer, ByVal XSrc As Integer, ByVal YSrc As Integer, ByVal dwRop As Long) As Integer Global Const SRCCOPY = &HCC0020 --------------- ScaleHeight Negative: If you set ScaleHeight negative to get positive Y up on a form or picture, the height of a label must be changed in code with a NEGATIVE height. Since the object height is explained as being measured positively from the top edge down, this is inconsistent. If you set the height with a positive number your program MIGHT run with garbage on the screen or crash with an error message. - Richard S. Johnson --------------- Unloading a Picture: To unload a picture from a Picture Box: Picture1.Picture = LoadPicture ("") (Jonathan Zuck) --------------- Zoom In/Out On A Picture: DefInt A-Z Declare Function StretchBlt% Lib "GDI" (ByVal hDC, ByVal X, ByVal Y, ByVal nWidth, ByVal nHght, ByVal hSrcDC, ByVal XSrc, ByVal YSrc, ByVal nSrcWidth, ByVal nSrcHeight, ByVal dwRop&) Const PIXEL = 3 Const NULL = 0& Const SRCCOPY = &HCC0020 picture1.ScaleMode = PIXEL picture1.autoredraw = 0 hDestDC = picture1.hDC X = 0: Y = 0 nWidth = picture1.scalewidth nHeight = picture1.scaleheight hSrcDC = picture1.hDC XSrc = 0: YSrc = 0 dwRop& = SRCCOPY 'stretches an icon to full size apierror = StretchBlt(picture1.hDC, 0, 0, picture1.scalewidth, picture1.scaleheight, picture1.hDC, 0, 0, 32, 32, SRCCOPY) (Robert Eineigl) --------------- 255-Character Limit?: Q: I seem to bump up against a 255 byte limit when a textbox on a VB server is hot-linked to a textbox on a VB client. Am I missing something? A: Change the Multiline property to True for the text box. Then you can get more than 255 bytes passed. (Art Baron) --------------- Copying a Line of Text: This function copies a line of text specified by LineNumber from Text control. The first line starts at zero. Declare Function GetFocus% Lib "user" () Declare Function SendMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, ByVal lParam As Any) Global Buffer As String Global Linenum As Long Function fGetLine (LineNumber As Long) Const MAX_CHAR_PER_LINE = 80 Const EM_GETLINE = &H400 + 20 byteLo% = MAX_CHAR_PER_LINE And (255) '[changed 3/25/92] byteHi% = Int(MAX_CHAR_PER_LINE / 256) '[changed 3/25/92, two lines:] Buffer$ = Chr$(byteLo%) + Chr$(byteHi%) + Space$(MAX_CHAR_PER_LINE - 2) Text1.SetFocus Pos& = SendMessage(GetFocus(), EM_GETLINE, CInt(LineNumber), Buffer$) aGetLine.Caption = "GetLine = " + Buffer$ fGetLine = Pos& End Function (Robert Eineigl, Microsoft Product Support) --------------- CtlName Access Via CDK: You cannot reliably access the CtlName property via a VBGetControlProperty function call of the CDK during Run Time. Use the "Tag" property instead. --------------- Cursor Missing: Setting a Borderstyle of none causes the caret to not appear if there is no text in the field. If you click in the text box or add a character, it appears. (John Hall) Additional "features": if you tab from one borderless Text box to another, the cursor will show up in the first character position as it should, but if you change Text boxes by clicking the mouse at the first character position, the cursor will NOT show up; if you click at some other character position, it WILL show up. Also, putting Text1.SelStart = 1 in GotFocus will cause the cursor to appear after the first character, but Text1.SelStart = 0 will not cause it to show up before the first character. (Nelson Ford) --------------- Cursor (text), Position: You can get the cursor position in a text box as follows: CursPos = Text1.SelStart --------------- Data Entry Masking: General - Declarations: Dim Txt as String Text1_GotFocus (): Text1.Text = "01/23" Txt$ = Text1.Text 'Txt$ = "01/23". This intitialization keeps KeyUp ' from kicking in until a key has been pressed. Text1_KeyPress: 'Assume cursor is on the "/" and "X" is pressed. Txt$ = Text1.Text 'Note: Text1.Text is still "01/23" at this point. Text1_KeyUp If Txt$ <> Text1.Text Then 'now Text1.Text is "01X/23" Call CursPos(Text1) 'and Txt$ is still "01/23" End If CursPos(Ctl as Control) i = 0 Do While Len(Txt$) > i i = i + 1 If Mid$(Txt$, i, 1) <> mid$(Ctl.Text, i, 1) Then pos = i Exit Do End If Loop if pos = 3 then 'Example of preventing an unwanted change: Ctl.Text = Txt$ 'Reset Text1.Text and Ctl.SelStart = pos - 1 'put the cursor back where it was. end if --------------- Data Entry Routine: .FRM Code: Sub Form_Load () SomeForm.Show InitStdControl AcctNo1 'Initialize standard edit controls InitStdControl AcctNo2 .. etc End Sub Sub AcctNo1_KeyPress (KeyAscii As Integer) StdEdit AcctNo1, KeyAscii 'Use standard editing procedure End Sub Module (.BAS) Code: DefInt A-Z Declare Function GetFocus% Lib "USER" () Declare Function SendMessage& Lib "USER" (ByVal hWnd%, ByVal wMsg% ByVal wParm%, ByVal lParm&) Const WM_USER = &H400 'Define the message number for Const EM_LIMITTEXT = WM_USER + 21 ' limiting the length of an edit ' Control Sub InitStdControl (Ctrl As Control) Ctrl.Width = 500 'Make field some standard length Ctrl.SetFocus 'Allow maximum of 14 characters Ok& = SendMessage(GetFocus(), EM_LIMITTEXT, 14, 0) .. more setup End Sub Sub StdEdit (Ctrl As Control, KeyAscii%) If KeyAscii >= 48 And KeyAscii <= 57 Then Ctrl.SelLength = 1 'This produces overstrike mode Else KeyAscii = 0 'Ignore non-numeric keys End if End Sub --------------- Data Entry Text Limit: Declare Function GetFocus% Lib "USER" () Declare Function SendMessage& Lib "USER" (ByVal hWnd%, ByVal wMsg%, ByVal wParm%, Byval lParm&) Const WM_USER = &H400 Const EM_LIMITTEXT = WM_USER + 21 In your Form_Load procedure: Form1.Show MaxLen% = (Whatever number you want) Text1.SetFocus txhWnd% = GetFocus() Ok& = SendMessage(txhWnd%, EM_LIMITTEXT, MaxLen%, 0) --------------- Fast Loading: For fastest loading of a file into a text box, use the following: OPEN File2Load$ For Binary as #1 If LOF(1) > 32768 Then MsgBox "File is too big." Else Buffer$ = Space$(LOF(1)) Get#1, 1, Buffer$ TextBox.Text = Buffer$ End If Close 1 This routine involves only 1 disk read and two string operations. The old WHILE...WEND loop required a disk read and two string operations for *EVERY* line in the file. -Jack Presley --------------- Flicker: Concatenating text in a Text Box can cause screen flicker. Instead use SelStart and SelLength - no flicker. An alternative is to concatenate the text in a string variable and then assign the string to the Text box when done. (Mark Novisoff) --------------- Flashing/Highlighting Text: 1. Sub FlashColor () If FlashState = True Then Label1.BackColor = RGB(255, 0, 0) Label1.ForeColor = RGB(255, 255, 255) Else Label1.BackColor = RGB(255, 255, 255) Label1.ForeColor = RGB(0, 0, 0) End If If FlashState = True Then FlashState = False Else FlashState = True End If End Sub 2. You can use the .SelLength and .SelStart to highlight and unhighlight a section of the text. Example of highlighting characters 11 - 15 in a Text box: Text1.SelStart = 11 Text1.SelLength = 5 --------------- Increasing Capacity to 64k: You can increase the capacity of a Text box to close to 64k: Declare Function GetFocus% Lib "user" () Declare Function SendMessage& Lib "user" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, lp As Any) Const WM_USER = &H400 Const EM_LIMITTEXT = WM_USER + 21 text1.SetFocus cbhWnd% = GetFocus() retVal& = SendMessage(cbhWnd%, EM_LIMITTEXT, &HFFFF, 0) The trick is to avoid the out of string space by careful use of the variable you read into the Text box from the file. A series of redimmed one-element string arrays would get around the string space limitation. (Robert Eineigl, Microsoft Product Support) --------------- Keeping Text Highlighted: Text in a Text box can be highlighted with SelStart and SelLength or with the mouse by the user, but it becomes un-highlighted if the Text box loses focus. So if you want to display an "are you sure" box before a user deletes highlighted text, for example, you have to use a round-about method: 1. Make a small form for asking for the confirmation. 2. Use the "floating form" routine in VB-Tips to make the form stay on top of the original form. 3. Bring the focus back to the original form and Text box and highlight the selected text again. 4. Set a flag so that no other processing is done until the confirmation has been done. 5. Wait for the user to click on the "Ok" or "No" (or whatever) button on the floating form. 6. To the user's eye, it appears that the confirmation box has popped up while the text has remained highlighted. The user can't tell that the floating form does not have the focus. (Nelson Ford) --------------- Line Number, Finding: The following code from Don Funk shows how to determine the current line number in a text box from the cursor position (also see "Mouse Click Detecting" following this tip): CharPos& = fGetSel() LineNumber& = fLineFromChar(CharPos&) Function fLineFromChar& (CharPos&) '* This function will return the line number of the line that '* contains the character whose location(index) specified in the '* third argument of SendMessage. If the third argument is -1, '* then the number of the line that contains the first character '* of the selected text is returned. Line numbers start at zero. Const EM_LINEFROMCHAR = &H400 + 25 Text1.SetFocus Pos% = SendMessage(GetFocus(), EM_LINEFROMCHAR, CharPos&, 0&) aLineFromChar.Caption = "Current Line = " + Str$(Pos%) fLineFromChar = Pos% End Function Function fGetLine$ (LineNumber As Long) '* This function returns a line of text specified by LineNumber '* from the edit control. The first line starts at zero. Const MAX_CHAR_PER_LINE = 80 Const EM_GETLINE = &H400 + 20 Text1.SetFocus Buffer$ = Space$(MAX_CHAR_PER_LINE) Pos% = SendMessage(GetFocus(), EM_GETLINE, LineNumber, Buffer$) fGetLine$ = Buffer$ End Function Function fGetSel& () '* This function returns the starting/ending position of the '* current selected text. This is the current location of the '* cursor if start is equal to ending. '* LOWORD-start position of selected text '* HIWORD-first no selected text Const EM_GETSEL = &H400 + 0 Text1.SetFocus location& = SendMessage(GetFocus(), EM_GETSEL, 0&, 0&) ending% = location& \ 2 ^ 16 starting% = location& Xor high% aGetSel.Caption = "Caret Location = " + Str$(starting%) fGetSel = location& End Function --------------- Number of Lines: The following routine from Don Funk returns the number of lines in a Text box: Declare Function GetFocus% Lib "user" () Declare Function SendMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, ByVal lParam As Any) Function fGetLineCount& () Const EM_GETLINECOUNT = &H400 + 10 Text1.SetFocus Pos% = SendMessage(GetFocus(), EM_GETLINECOUNT, 0&, 0&) fGetLineCount = Pos% End Function --------------- Read-Only Text Box: In a text box control, if you set Enabled = FALSE and MultiLine = TRUE, the contents of the text box control are not greyed and the mousepointer stays an arrow when over this text box control. Essentially, it's a read-only text box control. This way I could make the text box control perform like a normal text box control when I want the user to be able to enter info, while other times it acts like a label when I don't want the user to be able to enter info. (Rick J Andrews) --------------- Tab Key Trapping: VB intercepts Tab and doesn't pass it to either the KeyPress or KeyDown events. But if you set the TabStop property on all controls on the form to FALSE, then suddenly the Tab key is passed through like a normal character. (Brett Foster) --------------- DoEvents () in a Timer: If you put a DoEvents () in a Timer routine, you allow the Timer to kick in again while you may still be in the Timer routine. To avoid this, use a flag to indicate if you are already in the Timer routine and if so, to exit the Sub. --------------- Resolution/Accuracy: The TIMER function provides a "tick" resolution (approx. 1/18th of a second), but actual mileage may vary due to the way Windows works; ie: if you invoke the TIMER function, you'll get an accurate reading of the ticks, but if you use a Timer *control*, you most likely won't get a hit exactly every TimerInterval milliseconds... (Mark Novisoff) --------------- Using the Timer: If you use a Timer with a small interval (eg: 1) and another application is active, Windows may not be able to respond at the specified duration and execution of the related code will be postponed until Windows can get to it. --------------- What is Knowledge Base?: Fm: Don Funk (SL) 76701,155 I work for Microsoft within Product Support. We receive a variety of questions over the phone from many customers daily. We have at our disposal a data base with a query engine that we can search on key words for a solution. You have access to this data base also by typing "GO MSKB" [at a CompuServe prompt]. This database not only includes "How to questions" but also includes bug reports, press releases, and general questions and documentations about the products. There are only about 100 articles for VB now but it is growing. The Quick Basic includes many many many more articles since it has been around for a long time. This Knowledge Base is the source of articles not only for Basic but for all the products at Microsoft. --------------- VB Book Reviews: "Visual Basic Programming" by Douglas Hergert, Bantam: Entry-level info for beginning programmers. Good review of fundamentals like I/O techniques, data structures, file access, and all VB controls including their properties & methods. (Rick Heil) "Visual Basic" by Steve Holzner, Brady Books: It assumes a moderate understanding of basic, so if you're totally new to basic this probably shouldn't be your first book on the subject. The latter half of the book goes deeper into topics like graphics. Good detail on advanced data handling, sorting (with algorithms), error handling & DDE. (Rick Heil) "Microsoft Windows Multimedia Programmer's Reference", available from Waldenbooks (ISBN 1-55615-389-9). Other recommended books: Waite's "Visual Basic How-To", Waite Group Press, ISBN 1-878739-09-3. for intermediate to advanced Visual Basic Workshop, MS Press, ISBN 1-55615-386-4. "for getting started" --------------- How to Get the Files: The following files are available on CompuServe on MSBASIC, DL's 1 and 5. The files are also available on disk from PsL. The two sources are not identical because PsL has screened out some duplication, old version, non-working files, etc, and may have files that the authors have not uploaded to CIS. If you are looking for one particular file and belong to CompuServe, the cheapest and fastest way to get it is from MSBASIC's DL's. From any CIS prompt, enter GO MSBASIC. From MSBASIC's Forum prompt, enter DL1 or DL5 and browse for the file name with the BRO command. For example, to find CtlWhWnd, you could enter BRO CTL*.* LIB:ALL to search all the DL's on MSBASIC. The MSBASIC sysops have also posted an index file of all the programs on all of MSBASIC's DL's. That list has the advantage of being more up to date than the list that follows, but it offers less information, the VB and non-VB files are mixed together, and the files are not categorized in any way. Still, it can make it easier to find some of the files listed below. To get a disk full of routines from PsL, use the disk number indicated. Note that all categories with the same disk number are on the same disk. See the "Background" in this file for ordering information. Please note that as new files are added to these disks by category each month, disks may have to be split up to make room. If ordering a disk for a specific file, you must specify that when ordering so that we can verify that it is still on the disk. If you specify multiple files and some are still on the disk, we will send the requested disk. The format for this section is to show the program name followed by the name of the file on the PsL disk, the author/publisher of the file, and the shareware registration fee, if any. Registration fees are meant to be sent directly to the author to become licensed to use a program beyond a reasonable evaluation period. Do not send those amounts to PsL. PsL charges a flat $5 disk fee. --------------- DLL's, Utilities: PsL Disk #3490 256PICBX (256PBX) $0 is a DLL with source that displays 256 color bitmaps in a VB Picture box control. Source for a sample app is included. CtlWhWnd (CTLWHWN) User Friendly, Inc. $0 is a DLL function that returns the hWnd for any control. This makes it easier to use API calls that modify controls. CRC32 Simms, Jeff $0 contains a DLL with source code to calculate CRC32 value of a file. DSound $0 is a DLL that allows you to play sound files through a PC's speaker. A sample SND file and sample VB code are included. Since digitized sound files from various sources (eg: Mac, Sound Blaster, Amiga) are all very similar, virtually all of them can be played with DSound. Filter (FLTRTPW) Smith, Wilson $0 is a DLL that can tell you if the default printer has changed. TPW source code for the DLL and sample VB code is included. Findapp Obeda, Ed $0 is a DLL and related VB code that prevents users from opening more than one copy of your application or of programs such as Calculator. GetWindowList 1.1 (GETWLST) Steinwart, Todd $0 is a DLL that lets you get a list of handles for active, visible windows. Sample usage code is included. HotKey.DLL (HKEYVB) Zuck, Jonathan $0 is designed to provide hotkey support to Visual Basic, which is not directly supported in VB. You can define multiple hot keys in your application and the DLL can be used by multiple applications simultaneously. An executable demo with several examples is included. HUGEARR.DLL Schmidt, Stephen $0 is a DLL which contains functions for creation, maintenance, and deletion of arrays larger than 64K, with more than 32k elements per dimension. It also lets you copy contiguous elements from a Huge Array into a specified part of a VB array and vice-versa. C source for the DLL is included. InOut Faggart, B.E. $0 is a DLL for I/O Port control. Sample VB source code is included. LZSSLib 1.0 Eschalon Development Inc. $25 is a file compression/decompression DLL for Windows programmers. It uses the LZSS algorithm. This should work with any language that supports DLL calling, such as Turbo Pascal, C/C++, Actor, Visual Basic, Realizer, and ObjectVision. MHelp MicroHelp, Inc. $0 is a DLL with VB versions of PEEK, POKE, INP, OUT, VARPTR, VARSEG, VARSEGPTR, SADD, SSEG and SSEGADD. Modalex $0 is a DLL that makes a VB app appear modal to the calling app. Multi.DLL (MULTDLL) Simms, Jeff $0 is a DLL for a multi-selection listbox. C source code and all VB Declares are included. This is simpler than using pure API calls as in MultiPik (disk #3880) on which this DLL is based, but MultiPik has the advantage of not requiring the (3k) DLL file to be distributed. Packit (PACK-VB) Taylor, Ian $0 contains a DLL that will compress/decompress files using Lempel-Ziv compression modified by adaptive Huffman coding. Prnset (PRNSTVB) $0 is a DLL that allows changes to the default printer settings. RunModal (RUNMODL) $0 contains a DLL that will allow you to call any EXE file and have it run as if it were a modal dialog box from the calling application. Your VB app can be called as dialog boxes from Word; your apps can call Notepad, etc. WIN31 is required. SounDLL White, Bill $0 is a sound driver (DLL) with VB source code to illustrate its use. TPW source code for the DLL is included. (This DLL can be used with other Windows languages.) Space & Time (SPCTIME) Barrett, Paul James $0 is a DLL that reports free disk space and will copy the date and/or time to the the Clipboard. C source code for the DLL is included, but no source code to illustrate calling the DLL from VB, so you will have to study the C code. Stsbar 2.0 Staffin, Ed $0-$10 is a DLL for a status bar. This is a bar that is used to display program parameter/function status, mouse coordinated, column/row numbers -- or any other text you want to assign to it. Toolwnd.DLL (TOOLWND) Donahue, Ray $35 lets you create nice looking toolbars and toolboxes with icons for each selection. (Not on this disk. Ask for disk #8807.) VBCards 1.01P Sands, Richard R. $0 is a card-deck DLL for writers of card games for Windows. The author hopes to create a public domain standard DLL so that each programmer does not have to crowd your hard disk with a new card-deck DLL. VBCards contains 52 cards, a Joker, seven card back designs, two indicator cards, some support routines, and a short sample program. Although the sample code is in Visual Basic, the DLL should be usable with other languages. VBLaunch (VBLAUNC) Simms, Jeff $0 adds a Launch item to the VB menu bar in which it lists everything in Program Manager. This lets you access all the programs from PM without leaving VB. Whether you need this feature or not, the source code that is included has many interesting routines in it. The DLL source (in C) is also included. VBPoint Zuck, Jonathan $0 is a DLL that adds pointer support to VB. LP2Str$ is a string function that copies data, pointed to by a so-called "Long Pointer" into a VB proprietary string variable. Str2LP is a subroutine that will copy data from one address to another. With this function you can create the CV? and BLOAD commands. --------------- DOS Access/Functions: PsL Disk #3557 DateTime (DATETIM) $0 shows how to get a file's date/time stamp and file size directly from within Visual Basic without resorting to a shell to DOS DIR command. Dirscan.BAS Rose, Joel $0 contains source code for a routine that scans a system's drives for a particular file or files. Filebox Wykes, Harry JF $0 shows how to create a Windows style file-open box using List boxes rather than the VB DriveBox and DirBox objects. QSearch Simms, Jeff $0 is a DLL that lets you search a drive for a file, search a file for text, and copy a file. Wildcards are supported. A sample app is included. VBDOS $0 is a DLL that allows you to get free disk space, file information, and change file attributes. Sample code is included. --------------- Font Utilities: PsL Disk #3557 EnumFonts (ENUMFNT) Telelink Systems $8 is a DLL which allows you to access the Windows API service EnumFonts. A demo is provided which shows how to find which typefaces and fonts are supported by the default printer. Source code is included. Fontro Young, Ted M. $0 contains VB source code to create a rotatable font that can print sideways. FontView 2.0 (FONTVW) Snider, Charles $0 is code for an application to find, display, and print Windows fonts. Rotfont Egger, Fredi $0 shows how to print text at any angle in VB apps to screen or printer. --------------- Networking/Communications: PsL Disk #3557 ADialer Lamson, George $0 is the source code and executable for a program to dial the phone. ComDemo 1.0 $0 illustrates the use of several COMM API functions. Includes source code and an executable file. Link'n Load (LINKLOD) Krumsee, Art $0 is VB code that shows how to establish a network link, run a specified application, and when done, unload the link. Network managers with VB only have to enter the application name into the code and compile it for this to work. NetPrint (NETPRNT) Krumsee, Art $0 is VB source that allows network users to link and unlink printers from within Windows. It includes declarations for 3 undocumented Windows API functions which should make this application network independent. Novell-VB (NOVL-VB) Johnson, L.J. $0 contains an example program with source code that allows you to attach to and de-attach from multiple servers. You can also set drive mappings on your primary server. Many of the basic Novell API's are demonstrated. VBMate $0 contains a library of routines and sample code for interfacing VB with EXTRA! for Windows EHLLAPI.DLL. This gives VB the ability to interact with a 3720 mainframe terminal session. VBTerm McGuinness, Charles $0 is source code for a simple VT100 terminal emulator. --------------- System: PsL Disk #3704 Drag'n'Drop (DRAGDRP) Bonner, Paul $0 shows how to add support for the Windows 3.1 File Manager drag-and-drop facility to a VB program. DragForm (DRAGFRM) Simms, Jeff $0 demonstrates how to drag a form without a title bar and dragging a textbox using similar code. ExWait $0 demonstrates how to use the Windows GetModuleUsage function to cause VB to wait until a SHELL command has finished. Fndwnd $0 shows how to look for a window based on Caption and/or ClassName. Ini_Art Jones, Tim $0 is a tutorial on writing and using INI profile files. Source code examples are included. KeyCode Mosher, Sue $0 displays the hex keycode for any key pressed. This is useful for any Windows programmer, but the included source code is VB. Landau $0 shows how to use drag and drop, how to link to the Windows calculator, notepad and winhelp, and how to use the clipboard. LstDrag Mosher, Sue $0 is code for dragging a line in a List box to another line with the mouse or cursor keys. LoadTime $0 is a text file that tells how to handle slow loading programs to keep the user from messing things up. ResCheck 1.0 (RESCHEK) Houck, Dan $0 is source for checking and displaying GDI and User Heap information in an icon. Shuffler (SHUFLER) Stine, Brian D. $0 is VB source code for a utility to shuffle (randomize) sound files assigned to system events. Requires Win31 and the Professional Tool Kit for VB. SySwitch 2.03 (SYSWTCH) Bonner, Paul $0 is VB source for a utility for changing Windows configurations. VBFindID (VBFNDID) Kitsos, Costas $0 is a utility which can be used to determine the IDs of VB Controls during design-time. With the ID's known, you can use API calls during run-time without having to use the clumsy SetFocus method or to employ an extra DLL for the purpose. VBMem 1.1 (VBMEM-S) Snider, Charles K. $0 is source for a VB app that displays Windows system information: the operating mode, presence of a math chip, and free memory. Compiled and run minimized, the display will constantly update the free memory number. WndFind User Friendly, Inc. $0 solves the problem of needing an exact match when using the AppActivate command. This can also be used to determine if your application is already running. Source is included. --------------- Time/Date Applications: PsL Disk #3704 Calendar Program (CAL-PRO) Meadows, Al $0 is the source code and executable for a calendar program. DateTime (DAT-TIM) Michna, Hans-Georg $0 is VB source to display the date and time in a variety of international or custom formats. GridCal Meadows/Graves $0 is source for making a calendar using the Grid custom control. SmallCal (SMALCAL) Meadows, Al $0 is source for a sample application which illustrates how to generate a calendar for a user-specified month. VB Clock 1.0 (VBCLOCK) Gagliano, Jim $0 is VB source for an app to display the date and time in a very small window. VBClock 1.1 (VBCLK) Holland, Sarah $0 contains source code and an executable for a Win31 utility that shows the date, time, and the percentage of free system resources remaining. Options include background/foreground color selection, font selection, alarms, and more. VB Popup Calendar (VBPCAL) Schueler, Don $0 is source for a small pop-up calendar. It is intended for popping up in date fields and returning the value to the field. --------------- Graphics/Video: PsL Disk #3788 3D4VB LAN Services $10 lets you create beautiful 3-D effects. More than 20 different routines are included to draw 3-D frames, shadowed boxes, borders, picture buttons, etched and shadowed text, and more. Interestingly, this is all done with VB code - no DLL's to keep track of. All source code is included. BMPKit Campbell, George $0 is a set of routines to scale, print, and display bitmaps. A demo and all source code are included. Burglar & Animate (BURGLAR) Campbell, George $? contains source for two demonstrations of simple animation using icons. FrameVB Ford, Nelson ASP $0 is a Module that will draw shadowed frames around controls to create the illusion of their being recessed. Also included is a routine for underlining a control such as a Text box. GetPut Martin, Wendell $0 is a routine that works like the graphic Get and Put statements in DOS Basic. Example code is included. Icon Browser (ICONBRW) $0 shows how to more easily extract icons from apps in Windows 3.1. IconDemo (ICONDEM) Young, Ted M. $0 is demo code to show how to draw graphics on a VB app's icon. IcoXtrc $0 is a routine for extracting icons from executables and DLLs. Marquee OsoSoft $0 is code for adding a moving banner to your VB applications. MetaLib 1.0 Sygenex $0 contains a set of routines that will allow you to create, draw, and save SDK (vector graphics) metafiles. PolyGone (POLYGO) Mack, Jim $0 is the source for an interesting graphics display. It also illustrates the use of graphics when iconized. Programmer's Workshop 1.0 (PROGWRK) Bushnaq, Firas $10 is a template that shows all available screen resolutions on your monitor to make is easy for you to size your forms and dialogue boxes. It has a ruler scale which shows twips, inches, points, picas, and pixels and can be used for conversions among those units of measure. RLE vs BMP (RLEBMP) $0 is simply an illustration of the huge difference in size between standard BMPs and compressed RLE files. Refresh $0 demonstrates the use of VB's implicit refresh versus an explicit refresh of a Text Box. Shades Stine, Brian $0 show how to give your forms a "brushed steel" background like the Borland custom controls. SSave User Friendly, Inc. $0 is a DLL that allows you to create Windows screen savers (like After Dark) in Visual Basic. TurboDXF (TURBODX) Dorosh, David $50 is a DLL that allows you to create standard DXF graphics files with up to 25 entities per drawing. It works with TP, VB, OV, Excel, C. (Not on this disk. Ask for disk #8842.) VBFlip $0 is an outstanding demonstration of using the BitBlt and StretchBlt API functions. The demo code displays a grid of tiles which you can flip over to show a graphic or which you can X out with a zooming red X. The code also shows how to store a series of images in a single picture file and use the individual images. VBMask $0 demonstrates how to create a mask from an image and a screen object using BitBlt. VBMenu $0 shows how to add graphic bitmaps and check mark substitutes to a menu. VBScroll (VBSCROL) $0 demonstrates an easy way to scroll/pan across a bitmap in a Picture Box. Waldo Melanson, Leo $0 is the source for an animation sequence in which Waldo walks across the form. WinkDemo (WNKDEMO) Campbell, George $0 is source that illustrates how to provide an animated screen while your VB application is loading. WMFPix $0 is a set of graphics icons in WMF format for use in your VB programs. --------------- Custom Controls: PsL Disk #3880 3D-VB Input 1.0b (3DVBIN) Opaque Software $15 is a Text Box type custom control with numerous optional 3D effects, formatted input, justification, and more. CBList 1.0. Waldmeyer, Manfred $58 allows you to create checkboxes or radiobuttons as a single custom control for VB. It supports 2D and 3D display, several standard box types, and bitmaps/icons picture property. This version works only in design-time. ClipSib 1.0 Funk, Keith $0 allows you to have overlapping controls, which VB does not normally support. VB code for an excellent demo of the routine is included. This code contains many other useful routines as well. Dialogs Kitsos, Costas $0 shows how to call the Win31 Common Dialogs of File Open, File Save, Printer Setup, Font Selection, and Color Selection without a Custom Control or DLL. DropDown (DROPDWN) $0 shows how to simulate a Combo box with a Text box and a List box. FOpen 1.1 Kitsos, Costas $0 is a set of forms, VB code, and API calls to create the standard Windows type of "Open File" dialog box. A demo is included. Gauge Mattila, Jussi $0 tells how to create a Meter box from two Picture boxes. Graphic 1.1 Cramp, Stephen $15 is a 256-color custom control. It also includes support for OS/2 BMP files. This version can only be used within VB. Registration gets a runtime module. GridPl $0 is a routine for entering data into a Grid control and to scroll the Grid with Cntl-Arrow or the mouse. Huge-MC Marquis Computing $0 is the VB Source code to a huge array manager for Windows 3.1. You can create huge arrays of up to 16MB on 386 and 1 MB on 286. MoveTxt Funk, Keith $0 shows how to use a Picture control to simulate dragging of a Text control (which normally cannot be dragged). MsgBox Editor (MSGBOX) Buhrer, Richard $0 makes it easy to set up MsgBoxes. Run this utility, click on the elements you want, add the text, and it generates the source code for you on the Clipboard for you to paste into your program. MultiPik (MULTIPK) Kitsos, Costas $0 shows how to create a multiple-selection List box with API calls. This is a frequently requested feature for VB. Source and demo included. GrafStat 1.0 (GRAF-ST) Object Productivity, Inc. $22-$32 is a custom control for displaying the status of a process. Two styles are offered: thermometer and needle. Grid Routines (GRIDRTN) Ford, Nelson ASP $0 is a set of routines for use with the Grid custom control. Routines include cursor key scrolling of the Grid, direct input to the Grid, add a row, delete a row, save to and load from disk, clear the Grid and more. Grid-VB $0 is a single control that lets you display data in columns and rows. This was originally given to beta testers by Microsoft, but not included in VB. Jonathan Zuck has made enhancements to it. HugeGrid (HUGEGRD) Ford, Nelson ASP $0 is sample code illustrating the integration of a Huge Array with a Grid control. Features include sorting, searching, adding, deleting and sorted inserting of records into the Huge Array, as well as paging and scrolling through data with the Grid. Picnic Funk, Keith $0 allows you to make picture controls look like VB forms. It demonstrates how to use the Win3 API functions to add a caption, sizing border and a minimize box and system menu to a picture control. Prntlbl $0 shows how to print captions on a Form using API calls instead of Label controls. The code looks enormously complicated to us, considering that you can do the same thing with a few straight VB commands, but maybe we are missing something. PropView (PROPVW) Microsoft $0 is a Custom Control that can display the properties of the current form and allows printing of the control properties. RandGrid (RANDGRD) Ford, Nelson ASP $0 is sample code illustrating the integration of a random access file and a Grid control. Spin Kitsos, Costas $0 is commented VB source code to create a Spin Button Control. No DLL/VBX files are required. ToolBar Murphy, Stephen $0 walks you through creating a toolbar using a picture box for the bar and BMP files for the buttons. Sample source code and BMP files are included. VBMeter $0 is a VBX that lets you create a meter type display with horizontal or vertical bars. VBWrite Simms, Jeff $0 contains source code that demonstrates adding MS Write as a edit control to a VB application. The advantage is that a Text box does not allow different fonts, etc., which Write allows. This code is still in the experimental stage. --------------- General Tools/Reference/Info: PsL Disk #3968 Additem $0 is VB code that illustrates a potential problem with adding fixed length strings to a List Box. APIFunc Solutions by Smith $15 is an API function database in Cardfile format. (Not on this Disk. Ask for disk #8841.) APIXref $0 is a Help file that cross-references the API calls listed in WINAPI.TXT, included on this disk. To use this file, while in VB, press F1 for Help, then Alt-F and O to open the file. App Shell 1.0 (APPSHEL) Presley, Jim $0 is VB source code for a sample application that can be used as a starting point for file-oriented applications. The app includes forms for file open/save, printing, printer selection and setup (including a custom DLL with C source) and print canceling and a main form that ties everything together with pull-down menus and related code. BitMap Young, Ted $0 is code for an "application" that simply illustrates the use of all the VB Picture Box functions. Click & Double-Click (CLK-DBL) Ford, Nelson ASP $0 is code that shows how to trap both the click and double-click events for a control. Color Calculator (COLCALC) Evers, Dave $0 displays color number for color combo selected on sliding bars. VB source code is included. Focus U-Turn (FOCUTRN) $0 demonstrates techniques for the use of Focus, and what to expect when you use it, particularly as it applies to attempts to verify data on exit from a control via LostFocus. GetDec MicroHelp, Inc. $0 eliminates unused DLL declarations from the Global module of your programs, resulting in a smaller EXE. Hyper (HYPR-VB) Martin, Wendell $0 shows how to add hypertext to your VB programs. RTFGen 1.0 Baldwin, Dave $0 converts plain ASCII files into Rich Text Format for use with the Windows Help Compiler. This is much easier than learning Word, or its equivalent. (Not on this disk. Ask for disk #8842.) Saveme Irwin, Gregg $0 provides you with a reminder, as well as the ability, to save a project before running it. Forgetting to save before running is a sure way to lose lots of work over time. While Irwin designed this as a stand-alone, it seems like it would be more effective to make it the start-up form in your program during the design and testing period. VBAPI 0.2 Pruitt, Steve $0 contains text of the Microsoft WINAPI.TXT file in the Windows .HLP format. It includes additions and corrections as of March 9, 1992, and contains definitions of functions, constants, and data structure for use within Visual Basic. VB Assistant (VB-ASST) English, Donald R. $0 displays a menu that lets you save and run the application you are working on, invoke an icon editor, or quickly gain access to all help and text files which relate to Visual Basic. Source code and executable are included. VBClean Hite, David $0 is source for an automatic save/load procedure needed to shrink VB executables. VBE Digital Solutions $15 adds two useful text options to VB. It allows you to save selected text to a file and to print selected text. These options will appear under the File Menu in VB. VBMacro Campbell, George $0 is a group of macros for the Windows macro recorder that can save you a lot of keystrokes when writing code in VB. WinAPI Microsoft Corporation $0 is a text file listing all the Windows 3 API declarations for VB. With these declarations in your VB code, you can perform many functions not otherwise available in VB. Win31API.TXT (WN31API) Microsoft Corporation $0 is a text file containing Windows 3.1 API declarations for VB. --------------- Miscellaneous: PsL Disk #3969 BaseCnv (BASECN) Lyle Lensen $0 is a routine for doing number base conversions. CFixVB Mack, Jim $0 is a routine to round Currency variables at a given number of decimal places. ASM and OBJ files and sample code included. CLBox $0 is source code for a Combo List Box with incremental search. It also shows how to quickly clear a List Box. Coerce $0 is a routine to force Currency variable amounts to 0, 1, 2, or 3 decimal digits of accuracy. DDE Utility (DDEUTIL) $0 shows how to do DDE in a generic fashion. Lets you test which DDE commands will work with specific applications. Prompts for appname, pathname, topic, item, execute string, window mode and attempts to conduct the DDE link. Source code included. DDE_VB Pleas, Keith R. $0 contains source code for prototyping DDE strings and exploring DDE connections. FixMenu Young, Ted M. $0 is code that shows how to get/change properties of menu items. GPIB $0 is a sample application that shows how VB can be used as a control and monitoring software. Ini_Mod1 (INIMOD) Berry, Forrest $0 is a VB module to create, read and update private INI files. You only need to add it to your project and call it. Sample code is inlcuded. MenuOn $0 shows how to disable and then enable the entire Menu Bar of a Form quickly and without flicker. MMPVB Turpin, Jerry $0 shows how to use the MultiMedia Extensions to play MID, WAV, and MMM files. NCalc Smaby, Marcus $0 demonstrates the implementation of a common keyboard handler. Numbers Computing, Marquis $0 is a set of routines to emulate binary coded decimal and IEEE string/numeric conversion functions (MKI$, etc.) found in QB and PDS but not in VB. PGuide $0 is the source code for the sample routines in the VB Programmer's Guide. Profile.BAS Vance, Rob is VB code (API calls) that makes it easy to store program data in WIN.INI. Search (SRCH-VB) $0 contains source code and a sample application for quickly searching a list box. Sndply (SNDPLAY) $0 demonstrates use of sndPlaySound() in VB to play WAV files. Text Style (TXTYLE) Irwin, Gregg $0 shows how to change the style of Text boxes to allow only uppercase or only lowercase letters, to limit the number of characters, and to create a password entry box. TxtFmt $0 controls text input formats. It formats numeric, dates, uppercase, length of input, literal constants, etc. VBArray Kitsos, Costas shows how to load/save an array of up to 64k in a single operation. VBMPU Demo (VBMPU) $0 contains an MPU-401 MIDI card demo and play-only source routines. Dynamic MIDI channel and velocity change are supported. VBXlDem $0 is source code to show how to access an embedded chart in Excel and link it to a basic picture control. VqStrings (VQSTRNG) $0 allows huge variable and fixed length strings for Visual Basic. VStrings delivers fast access and return error codes compatible with Visual Basic's 'On Error GoTo'. #4062 Miscellaneous, continued ComServ Buck, Alan $0 is the source code for a VB application for keeping track of Zip files. Studying other apps is a good way to learn. GPMVB1 $0 is source that shows how to use VB to create and edit a form-based document in Microsoft Word for Windows. GMPVB2 $0 shows how to use DDE to get and manipulate data from Word for Windows. VBInst 1.00 Kallonen, Jari $0 is the source code for a VB app to install a program from a floppy to a specified subdirectory. It can also create a Program Manager group. Winstall.Frm $0 is source code for a program to find and run installation programs on program disks. --------------- Database, Data Entry, Sorting: PsL Disk #4062 Btcreate (BTCREAT) Highsmith, J. $0 is sample source for the use of Btrieve create in VB. Requires the Btrieve for Windows DLL. BtrTest Meyer, David $0 is source code for a Btrieve for Windows sample program. CallBT Kubelka, Bob $0 contains a function which simplifies using Btrieve with Visual Basic. Requires Btrieve for Windows. EditDem Milligan, Keith $0 is a set of data entry control routines for VB: date entry, price/quantity, currency, two and four decimal places, string to a specified length, uppercase text input, and others. Paradox Engine Demo (PDOXED) Jaster, John contains a DLL and source code for a simple demonstration of the Paradox engine. PXCreate (PXCREAT) Nech, Jim $0 provides a way to use the Paradox Engine to create Paradox tables in Visual Basic. Source Code is included, and VBRUN is required. SQL Query (SQLQRY) $0 is an upgrade of the Query VB program in MS's VB SQL Server DLL kit (required to run this file). It improves the handling of returned rows. SQLGrid Hyland, Bob $0 shows how to use a GRID control (on disk #3880) with the VBQSL SQL Server (from Microsoft). VBDB $0 is a set of forms and code for a dBase engine. A lot of coding has gone into it. The sample application is about 85% complete; the engine is completely done. VB-ISAM Eibl, Gunter $0 contains the source code for an ISAM-emulation, using the rules of Basic PDS 7.1. The emulation needs the Btrieve DLL. An an example is included to see how to use Btrieve from Visual Basic. VB-Paradox Engine Interface (VBPXENG) Dooley, Sharon F. $0 is a set of declarations that create an interface to the Paradox Engine. It will translate between VB and Paradox data types. VBSort Ford, Nelson $0 is the code and a sample application for sorting both a List box and an array. Testing shows that it takes 8 times longer to sort a List box. WinBtrv Novell $0 is a DLL to allow using Btrieve in VB applications. XQLM Declarations (XQLMVB) $0 are the VB declarations for working with Novell XQL. --------------- Printing: #4226 Code.Print 1.6a (CODEPRN) Caladonia Systems, Inc. $35 lets you print your VB source code with a wide range of options, including titles, headers, footers, project name, font control, user-defined pagination, margins and line spacing, the ability to print entire applications or single modules or routines; to sort source code by subroutine; to serialize backups of source code files and to print an index of functions and subroutines. Endprn $0 is source code for a small utility for cancelling a print job. HPEnv 2.0 (HPENV-S) Himowitz, Michael J. $0 is VB source for an application that prints envelopes in landscape mode without having to change printer setup. This is also on a Windows applications disk. HPESC O'Rourke, Peter $0 explains how to send ESCape sequences from Visual Basic to an HPIII printer. Pict_t Santamarina, George F. $0 demonstrates how to print bitmaps using Windows 3.1 Common Dialog routines. Print Clipboard (PRINTCB) Barnett, Clifford P. $0 contains source code and an executable for printing the contents of the Windows Clipboard. Text and graphic dumps are both supported. PrSetup Schwartz, Corey $0 is a DLL and supporting code to add to your applications to allow the user to select printers, as well as to adjust printer setup and configuration. All C and VB source code is included. PrtList Mattila, Jussi $0 is a set of printer selection procedures for VB. Psetup revC Kitsos, Costas $0 is a DLL that lets you invoke the Windows standard Printer Setup Dialog. Sample source code is included. PSOut Cramp, Stephen $0 illustrates a method of printing bitmap files up to 32k in size. It has been tested on PostScript and HP LaserJet printers only. PWrap Wagner, Richard $0 is a routine for sending text to the printer with word wrap at word breaks, as delimited by spaces. VBBook (VBBOOKS) Scott, Dennis $0 is source code for an app that lets you print ASCII text files to an HPLJ printer in booklet format. VBPDemo Obeda, Ed $0 shows how to calculate the default printer's lines per page under the declared FONTNAME and FONTSIZE. It will also a allow the user to ABORT the print job, thereby flushing the printer buffer. VB Printer Control (VBPRINT) Harrington, Dennis $0 demonstrates how to put a laser printer into Landscape mode and how to select a paper bin without going through the Windows print manager. --------------- Other: (The programs in this section are large enough to be on disks by themselves. Their disks numbers are indicated below. The dollar amounts shown are for registering these shareware programs with their authors. PsL charges $5/disk.) BasicBasic Davidsaver, Mark $15 #4204 lets you compile BASIC programs for DOS or Windows. Not all "standard" BASIC commands are supported. This is a very easy way to develop small programs without regard for which environment they will run under. Several sample files are included for compiling, and ASCII source code is accepted. Modified Setup1 Johnson, L.J. $0 #4463 adds new features to the PTK Setup program. It provides options to do a Full Installation or Update Files, and allows users to install in either a new program group or an existing group. Other features include many bargraph improvements, 3-D controls, automatic disk space calculation, and much more. PTK Control Center Obeda, Ed $11 #4451 makes it easy to add Professional Toolkit Custom Controls to your VB apps. In addition, commonly used tools can be saved and automatically selected on startup. Quasar SQL for Windows Stellar Industries $198-$298 #3942 makes it easy for programmers to create relational database applications for Windows. It takes full advantage of Windows' pull down menus, dialog boxes, icons, and on-line help. It features a clear error-handling and reporting interface, and a complete transaction management facility. It provides a full-featured relational database engine, a multiple document editor, implementation of the American Standard SQL specification, and advanced indexing capabilities. This program gives developers a choice of front-end development environments, including C, C++, Visual Basic, Pascal, and more. Sample files are included. A hard disk or high density floppy is required. Quick and Dirty Help Allen, Phil $20 #4142 lets you use any ASCII editor to produce RTF files for the creation of Windows 3.0 Help Files. With QDHelp, you can easily create help files with links, popups, bitmaps, search keys, and browse sequences. The MS Help Compiler is required. (Also see WI-01: #3477, Xantippe, which can create RTF files very easily.) SVInstal Soft Ventures $40 #4377 installs program and data files from a distribution floppy to a hard drive. It creates directories and copies files to them, builds a Windows program group and loads icons, and presents a README.TXT file in a pop-up window. VBToolBox Jurcik, Hal $10 #4517 automates the process of saving and running code in VB. Functions are also included for calling text files, Icon editors, add-on help files, or just about any program from within the Visual Basic editing environment. vxBase Comsoft Inc. $50 #7614/3946 [2 disks] contains a DLL and sample files for creating dBASE compatible applications. Features include up to eight active browse windows, large indexing buffer, memo functions, multi-user options, and much more. DataWorks 1.25 is on the second disk. It is an xBase power user's tool to be used by developers building applications with VXBASE for Visual Basic. Windows Enhanced Dialog Library Smedley, Mike $60 #3787 is an extensive DLL designed to make dialog box data entry much easier for both the programmer and the user. Features include formatted data entry, field validation, automatic updating of variables, numeric data entry, overtype mode, context-sensitive help, and much more. A demo executable is included. WinHelp+ Stedy Software & Systems $10 #7776/4454 ties together tools used in creating help files. It has a pushbutton interface for calling up editors for RTF and BMP and other necessary input files, QDHelp (above), and the Help Compiler itself. VBRUN is included on this disk. --------------- PsL's September 1992 Visual Basic Disk: This disk contains new programs being added to PsL's Visual Basic section. Dialogs VB: Controls Kitsos/Johnson; $0 contains example code for using common dialogs with API calls without the need for a separate DLL. This update has a lot of improvements, including more error checking. OpenDlg VB: Custom Controls Zane Thomas; $0 is a DLL for the Open and Save-As common dialogs. PSPFrm VB: Tools $0 is source code that allows printing a form, including border and caption, to a PostScript or PCL printer. Print size is adjustable. SysMenu VB: Controls Jeff Simms; $0 is a DLL for adding menu items to a form's system control box. TimeQueue & TextKey (TIMETXT) Curlew Software $0-$10 are two VB controls. TextKey lets you attach a prepared text file to it during design time. Specified text will be highlighted, the mousepointer changes shape over it, and a click event can be fired on the highlighted text, allowing you to create hypertext applications, etc. TextKey has many other interesting features, such as margin control and the ability to page through the text file. TimeQueue lets you execute events at specified time intervals. In the sample file included with this code, TimeQueue is used to change the pages displayed in the TextKey box. TextKey and TimeQueue are in a single VBX. This is a unique, must-have add-on for VB programmers. ToolButton 1.0 (TOOLBUT) VB: Controls Brett Foster; $0-$? let you have standard toolbar buttons. Features include nine standard command buttons, six different button states generated from a single supplied bitmap for custom buttons, MouseDown event for help messages, on-line help reference, and more. ---------------